├── .gitignore ├── JsPosedAnnotation ├── .gitignore ├── build.gradle └── src │ └── main │ └── java │ └── com │ └── wrbug │ └── jsposedannotation │ ├── Constant.java │ ├── Executable.java │ ├── JavaClass.java │ ├── JavaMethod.java │ └── RealValueWrapper.java ├── JsPosedCompile ├── .gitignore ├── build.gradle ├── libs │ └── android.jar └── src │ └── main │ └── java │ └── com │ └── wrbug │ └── jsposedcompile │ ├── DefaultValue.java │ ├── JavaClassListUtils.java │ ├── JsPosedProcessor.java │ └── methodextension │ ├── CompoundButtonExtension.java │ ├── MethodExtension.java │ ├── MethodExtensionManager.java │ ├── RealValueWrapperExtension.java │ ├── ViewExtension.java │ └── XposedHelpersExtension.java ├── LICENSE ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── jsposed ├── .gitignore ├── bintray.gradle ├── build.gradle ├── gradle.properties ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── wrbug │ │ └── jsposed │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── wrbug │ │ │ └── jsposed │ │ │ ├── ArrayManager.java │ │ │ ├── ClassUtils.java │ │ │ ├── CommonMethodHook.java │ │ │ ├── JaveMethodWapper.java │ │ │ ├── JsNumberUtils.java │ │ │ ├── JsPosedConfig.java │ │ │ ├── JsPosedExecutor.java │ │ │ ├── JsPosedReceiver.java │ │ │ ├── LogUtils.java │ │ │ └── jscall │ │ │ ├── JsBundle.java │ │ │ ├── JsIntent.java │ │ │ ├── context │ │ │ ├── JsActivity.java │ │ │ └── JsContext.java │ │ │ ├── view │ │ │ ├── JsButton.java │ │ │ ├── JsCheckBox.java │ │ │ ├── JsCompoundButton.java │ │ │ ├── JsTextView.java │ │ │ ├── JsView.java │ │ │ └── JsViewGroup.java │ │ │ └── xposed │ │ │ ├── Env.java │ │ │ ├── JsPosedBridge.java │ │ │ └── JsPosedHelpers.java │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── wrbug │ └── jsposed │ └── ExampleUnitTest.java ├── jsposedgradleplugin ├── .gitignore ├── bintray.gradle ├── build.gradle ├── gradle.properties ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── wrbug │ │ └── jsposedgradleplugin │ │ └── ExampleInstrumentedTest.java │ └── main │ ├── groovy │ └── com │ │ └── wrbug │ │ └── jsposedgradleplugin │ │ ├── JsPosedPlugin.groovy │ │ └── MergeJavaMethodTransform.groovy │ └── resources │ └── META-INF │ └── gradle-plugins │ └── com.wrbug.jsposedgradleplugin.properties ├── readme.md ├── sample ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── wrbug │ │ └── jsposeddemo │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ └── xposed_init │ ├── java │ │ └── com │ │ │ └── wrbug │ │ │ └── jsposeddemo │ │ │ ├── Main2Activity.java │ │ │ ├── MainActivity.java │ │ │ ├── Test.java │ │ │ └── XposedInit.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ └── activity_main2.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── wrbug │ └── jsposeddemo │ └── ExampleUnitTest.java ├── settings.gradle └── tmp.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches/build_file_checksums.ser 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | .DS_Store 9 | /build 10 | /captures 11 | .externalNativeBuild 12 | -------------------------------------------------------------------------------- /JsPosedAnnotation/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /JsPosedAnnotation/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java-library' 2 | 3 | dependencies { 4 | implementation fileTree(dir: 'libs', include: ['*.jar']) 5 | compileOnly 'de.robv.android.xposed:api:82' 6 | implementation 'org.mozilla:rhino:1.7.10' 7 | } 8 | 9 | sourceCompatibility = "7" 10 | targetCompatibility = "7" 11 | -------------------------------------------------------------------------------- /JsPosedAnnotation/src/main/java/com/wrbug/jsposedannotation/Constant.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposedannotation; 2 | 3 | public class Constant { 4 | public static final String BUILD_PACKAGE = "com.wrbug.jsposed.jclass.build"; 5 | public static final String JAVA_METHOD_ARRAY_CLASS_NAME = "JavaMethodArray"; 6 | public static final String JAVA_METHOD_ARRAY_FIELD_NAME = "javaMethods"; 7 | public static final String JAVA_METHOD_ARRAY_GET_METHOD_NAME = "get"; 8 | public static final String ORIGIN_VALUE_NAME = "value0"; 9 | } 10 | -------------------------------------------------------------------------------- /JsPosedAnnotation/src/main/java/com/wrbug/jsposedannotation/Executable.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposedannotation; 2 | 3 | import org.mozilla.javascript.Function; 4 | 5 | public interface Executable { 6 | void run(String js); 7 | 8 | void call(Function call, Object... args); 9 | } 10 | -------------------------------------------------------------------------------- /JsPosedAnnotation/src/main/java/com/wrbug/jsposedannotation/JavaClass.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposedannotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Target(ElementType.TYPE) 9 | @Retention(RetentionPolicy.CLASS) 10 | public @interface JavaClass { 11 | Class value(); 12 | 13 | String name() default ""; 14 | } 15 | -------------------------------------------------------------------------------- /JsPosedAnnotation/src/main/java/com/wrbug/jsposedannotation/JavaMethod.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposedannotation; 2 | 3 | import de.robv.android.xposed.callbacks.XC_LoadPackage; 4 | 5 | public abstract class JavaMethod { 6 | protected static Executable mJsPosedExecutor; 7 | protected static XC_LoadPackage.LoadPackageParam mParam; 8 | 9 | public void setJsPosedExecutor(Executable jsPosedExecutor) { 10 | mJsPosedExecutor = jsPosedExecutor; 11 | } 12 | 13 | public void setParam(XC_LoadPackage.LoadPackageParam param) { 14 | mParam = param; 15 | } 16 | 17 | public String getJavaMethodName() { 18 | return getClass().getSimpleName(); 19 | } 20 | 21 | 22 | @Override 23 | public String toString() { 24 | return "name=" + getJavaMethodName() + "," + getClass().getName(); 25 | 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /JsPosedAnnotation/src/main/java/com/wrbug/jsposedannotation/RealValueWrapper.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposedannotation; 2 | 3 | public interface RealValueWrapper { 4 | Object getRealValue(); 5 | } 6 | -------------------------------------------------------------------------------- /JsPosedCompile/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /JsPosedCompile/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java-library' 2 | 3 | dependencies { 4 | implementation 'com.squareup:javapoet:1.10.0' 5 | implementation 'com.google.auto.service:auto-service:1.0-rc3' 6 | implementation fileTree(include: ['*.jar'], dir: 'libs') 7 | implementation project(':JsPosedAnnotation') 8 | implementation 'org.mozilla:rhino:1.7.10' 9 | compileOnly 'de.robv.android.xposed:api:82' 10 | implementation 'com.google.code.gson:gson:2.8.5' 11 | } 12 | 13 | sourceCompatibility = "7" 14 | targetCompatibility = "7" 15 | -------------------------------------------------------------------------------- /JsPosedCompile/libs/android.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WrBug/JsPosed/ce9569f3e2764ea7cf18351eaf6c1914c375942a/JsPosedCompile/libs/android.jar -------------------------------------------------------------------------------- /JsPosedCompile/src/main/java/com/wrbug/jsposedcompile/DefaultValue.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposedcompile; 2 | 3 | import com.squareup.javapoet.TypeName; 4 | 5 | public class DefaultValue { 6 | 7 | public static String get(Class returnType) { 8 | String defaultValue = "0"; 9 | if (returnType.isPrimitive()) { 10 | if (returnType == void.class) { 11 | defaultValue = ""; 12 | } else if (returnType == boolean.class) { 13 | defaultValue = "false"; 14 | } 15 | } else if (returnType == String.class) { 16 | defaultValue = "\"\""; 17 | } else { 18 | defaultValue = "null"; 19 | } 20 | return defaultValue; 21 | } 22 | 23 | public static String get(TypeName returnType) { 24 | if (returnType == TypeName.VOID) { 25 | return ""; 26 | } 27 | if (returnType == TypeName.BOOLEAN) { 28 | return "false"; 29 | } 30 | if (returnType.isPrimitive()) { 31 | return "0"; 32 | } 33 | if (returnType.toString().equals(String.class.toString())) { 34 | return "\"\""; 35 | } 36 | return "null"; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /JsPosedCompile/src/main/java/com/wrbug/jsposedcompile/JavaClassListUtils.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposedcompile; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.reflect.TypeToken; 5 | 6 | import java.io.File; 7 | import java.io.FileInputStream; 8 | import java.io.FileNotFoundException; 9 | import java.io.FileOutputStream; 10 | import java.io.IOException; 11 | import java.util.HashMap; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | public class JavaClassListUtils { 16 | private static final String fileName = "javaclass.txt"; 17 | 18 | static { 19 | File file = new File(fileName); 20 | if (!file.exists()) { 21 | try { 22 | file.createNewFile(); 23 | } catch (IOException e) { 24 | e.printStackTrace(); 25 | } 26 | } 27 | } 28 | 29 | public static void saveJavaClass(Class target, String className) { 30 | Map map = getList(); 31 | if (map.containsKey(target.getName())) { 32 | return; 33 | } 34 | map.put(target.getName(), className); 35 | writeList(map); 36 | } 37 | 38 | private static void writeList(Map map) { 39 | String result = new Gson().toJson(map); 40 | try { 41 | FileOutputStream fileOutputStream = new FileOutputStream(new File(fileName)); 42 | fileOutputStream.write(result.getBytes()); 43 | fileOutputStream.flush(); 44 | fileOutputStream.close(); 45 | } catch (IOException e) { 46 | e.printStackTrace(); 47 | } 48 | } 49 | 50 | 51 | public static Map getList() { 52 | try { 53 | FileInputStream fileInputStream = new FileInputStream(new File(fileName)); 54 | int size = fileInputStream.available(); 55 | if (size <= 0) { 56 | return new HashMap<>(); 57 | } 58 | byte[] data = new byte[size]; 59 | fileInputStream.read(data); 60 | fileInputStream.close(); 61 | String result = new String(data); 62 | Map map = new Gson().fromJson(result, new TypeToken>() { 63 | }.getType()); 64 | return map; 65 | } catch (Exception e) { 66 | e.printStackTrace(); 67 | } 68 | return new HashMap<>(); 69 | } 70 | 71 | 72 | private static String join(List list) { 73 | StringBuilder builder = new StringBuilder(); 74 | for (String s : list) { 75 | builder.append(s).append(","); 76 | } 77 | if (builder.length() > 0) { 78 | builder.deleteCharAt(builder.length() - 1); 79 | } 80 | return builder.toString(); 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /JsPosedCompile/src/main/java/com/wrbug/jsposedcompile/JsPosedProcessor.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposedcompile; 2 | 3 | import android.os.Bundle; 4 | 5 | import com.google.auto.service.AutoService; 6 | import com.squareup.javapoet.CodeBlock; 7 | import com.squareup.javapoet.JavaFile; 8 | import com.squareup.javapoet.MethodSpec; 9 | import com.squareup.javapoet.TypeName; 10 | import com.squareup.javapoet.TypeSpec; 11 | import com.wrbug.jsposedannotation.Constant; 12 | import com.wrbug.jsposedannotation.JavaClass; 13 | import com.wrbug.jsposedannotation.JavaMethod; 14 | import com.wrbug.jsposedannotation.RealValueWrapper; 15 | import com.wrbug.jsposedcompile.methodextension.MethodExtensionManager; 16 | 17 | import java.io.File; 18 | import java.io.IOException; 19 | import java.lang.reflect.Constructor; 20 | import java.lang.reflect.Method; 21 | import java.lang.reflect.Type; 22 | import java.util.ArrayList; 23 | import java.util.Arrays; 24 | import java.util.HashMap; 25 | import java.util.HashSet; 26 | import java.util.List; 27 | import java.util.Map; 28 | import java.util.Set; 29 | 30 | import javax.annotation.processing.AbstractProcessor; 31 | import javax.annotation.processing.Filer; 32 | import javax.annotation.processing.Messager; 33 | import javax.annotation.processing.ProcessingEnvironment; 34 | import javax.annotation.processing.Processor; 35 | import javax.annotation.processing.RoundEnvironment; 36 | import javax.lang.model.SourceVersion; 37 | import javax.lang.model.element.Element; 38 | import javax.lang.model.element.Modifier; 39 | import javax.lang.model.element.TypeElement; 40 | import javax.lang.model.type.MirroredTypeException; 41 | import javax.lang.model.util.Elements; 42 | import javax.tools.Diagnostic; 43 | 44 | import static com.wrbug.jsposedannotation.Constant.ORIGIN_VALUE_NAME; 45 | 46 | @AutoService(Processor.class) 47 | public class JsPosedProcessor extends AbstractProcessor { 48 | private Messager mMessager; 49 | private Elements mElementUtils; 50 | private Filer mFiler; 51 | private Map javaMethodMap = new HashMap<>(); 52 | private String moduleName; 53 | private List objectFinalMethodNames; 54 | private MethodExtensionManager mMethodExtensionManager; 55 | 56 | @Override 57 | public synchronized void init(ProcessingEnvironment processingEnvironment) { 58 | super.init(processingEnvironment); 59 | // 生成文件所需 60 | javaMethodMap.clear(); 61 | mFiler = processingEnv.getFiler(); 62 | mMessager = processingEnv.getMessager(); 63 | mMethodExtensionManager = new MethodExtensionManager(mMessager); 64 | mElementUtils = processingEnv.getElementUtils(); 65 | moduleName = processingEnv.getOptions().get("moduleName"); 66 | if (moduleName != null) { 67 | moduleName = moduleName.replace("-", "_").toLowerCase(); 68 | } 69 | initObjectFinalMethod(); 70 | } 71 | 72 | 73 | private void initObjectFinalMethod() { 74 | objectFinalMethodNames = new ArrayList<>(); 75 | Method[] declaredMethods = Object.class.getDeclaredMethods(); 76 | List tmp = new ArrayList<>(Arrays.asList(declaredMethods)); 77 | Method[] methods = Object.class.getMethods(); 78 | tmp.addAll(Arrays.asList(methods)); 79 | for (Method method : tmp) { 80 | if (java.lang.reflect.Modifier.isFinal(method.getModifiers())) { 81 | objectFinalMethodNames.add(getMethodName(method)); 82 | } 83 | } 84 | } 85 | 86 | @Override 87 | public Set getSupportedAnnotationTypes() { 88 | Set supportAnnotations = new HashSet<>(); 89 | supportAnnotations.add(JavaClass.class.getCanonicalName()); 90 | return supportAnnotations; 91 | } 92 | 93 | @Override 94 | public SourceVersion getSupportedSourceVersion() { 95 | return SourceVersion.latestSupported(); 96 | } 97 | 98 | @Override 99 | public boolean process(Set set, RoundEnvironment roundEnvironment) { 100 | Set javaClassElements = roundEnvironment.getElementsAnnotatedWith(JavaClass.class); 101 | for (Element element : javaClassElements) { 102 | JavaClass javaClass = element.getAnnotation(JavaClass.class); 103 | Class targetClass = null; 104 | try { 105 | targetClass = javaClass.value(); 106 | } catch (MirroredTypeException e) { 107 | mMessager.printMessage(Diagnostic.Kind.NOTE, e.getTypeMirror().toString()); 108 | try { 109 | targetClass = Class.forName(e.getTypeMirror().toString()); 110 | } catch (ClassNotFoundException e1) { 111 | e1.printStackTrace(); 112 | } 113 | } 114 | if (targetClass == null || targetClass.isInterface()) { 115 | continue; 116 | } 117 | JavaClassListUtils.saveJavaClass(targetClass, element.toString()); 118 | List list = new ArrayList<>(); 119 | buildDefaultMethod(list, javaClass, element, targetClass); 120 | buildWrapperMethod(list, element, targetClass); 121 | buildConstructorMethod(list, element, targetClass); 122 | Map> methodMap = getMethod(targetClass); 123 | for (Map.Entry> entry : methodMap.entrySet()) { 124 | String methodName = entry.getKey(); 125 | Map existMethodMap = new HashMap<>(); 126 | List methodList = entry.getValue(); 127 | for (Method declaredMethod : methodList) { 128 | if (!java.lang.reflect.Modifier.isPublic(declaredMethod.getModifiers()) 129 | || (declaredMethod.getModifiers() & 4096) != 0) { 130 | continue; 131 | } 132 | String existMethodMapKey = declaredMethod.getName() + Arrays.toString(declaredMethod.getParameterTypes()); 133 | MethodSpec methodSpec = existMethodMap.get(existMethodMapKey); 134 | if (methodSpec != null) { 135 | list.remove(methodSpec); 136 | } 137 | boolean staticMethod = java.lang.reflect.Modifier.isStatic(declaredMethod.getModifiers()); 138 | StringBuilder code = new StringBuilder(); 139 | MethodSpec.Builder builder = MethodSpec.methodBuilder(declaredMethod.getName()) 140 | .addModifiers(Modifier.PUBLIC) 141 | .returns(declaredMethod.getReturnType()); 142 | if (!staticMethod) { 143 | String defaultValue = DefaultValue.get(declaredMethod.getReturnType()); 144 | code.append("if(").append(ORIGIN_VALUE_NAME).append("==null){return ").append(defaultValue).append(";}"); 145 | } 146 | if (declaredMethod.getReturnType() != void.class) { 147 | code.append("return "); 148 | } 149 | Type[] genericExceptionTypes = declaredMethod.getGenericExceptionTypes(); 150 | if (genericExceptionTypes != null && genericExceptionTypes.length > 0) { 151 | for (Type exceptionType : genericExceptionTypes) { 152 | builder.addException(exceptionType); 153 | } 154 | } 155 | if (staticMethod) { 156 | builder.addModifiers(Modifier.STATIC); 157 | } 158 | Class[] parameterTypes = declaredMethod.getParameterTypes(); 159 | int a = 0; 160 | StringBuilder params = new StringBuilder(); 161 | for (int i = 0; i < parameterTypes.length; i++) { 162 | Class parameterType = parameterTypes[i]; 163 | String name = "arg" + a++; 164 | Class tmp = parameterType; 165 | if (parameterType == int.class) { 166 | tmp = long.class; 167 | parameterTypes[i] = tmp; 168 | } 169 | builder.addParameter(tmp, name); 170 | if (parameterType.isPrimitive()) { 171 | params.append("(").append(parameterType.getSimpleName()).append(")"); 172 | } 173 | params.append(name).append(","); 174 | } 175 | existMethodMapKey = declaredMethod.getName() + Arrays.toString(parameterTypes); 176 | MethodSpec spec = existMethodMap.get(existMethodMapKey); 177 | if (spec != null) { 178 | continue; 179 | } 180 | if (params.length() > 0) { 181 | params.deleteCharAt(params.length() - 1); 182 | } 183 | code.append(staticMethod ? targetClass.getName() : ORIGIN_VALUE_NAME).append(".").append(declaredMethod.getName()).append("(").append(params).append(");\n"); 184 | builder.addCode(code.toString()); 185 | MethodSpec build = builder.build(); 186 | list.add(build); 187 | existMethodMap.put(existMethodMapKey, build); 188 | } 189 | } 190 | 191 | mMethodExtensionManager.build(list, targetClass); 192 | buildJavaFile(element.getSimpleName().toString(), targetClass, list); 193 | String jsName = javaClass.name(); 194 | if (jsName.isEmpty()) { 195 | jsName = element.getSimpleName().toString(); 196 | } 197 | javaMethodMap.put(jsName, element.toString()); 198 | } 199 | buildMap(); 200 | mMessager.printMessage(Diagnostic.Kind.NOTE, javaMethodMap.toString()); 201 | return true; 202 | } 203 | 204 | 205 | private Map> getMethod(Class clazz) { 206 | List names = new ArrayList<>(); 207 | Class currentClass = clazz; 208 | Map> map = new HashMap<>(); 209 | while (currentClass != Object.class) { 210 | Method[] declaredMethods = currentClass.getDeclaredMethods(); 211 | List tmp = new ArrayList<>(Arrays.asList(declaredMethods)); 212 | Method[] methods = currentClass.getMethods(); 213 | tmp.addAll(Arrays.asList(methods)); 214 | for (Method method : tmp) { 215 | String name = getMethodName(method); 216 | if (objectFinalMethodNames.contains(name)) { 217 | continue; 218 | } 219 | if (names.contains(name)) { 220 | continue; 221 | } 222 | names.add(name); 223 | List list = map.get(method.getName()); 224 | if (list == null) { 225 | list = new ArrayList<>(); 226 | } 227 | list.add(method); 228 | map.put(method.getName(), list); 229 | } 230 | currentClass = currentClass.getSuperclass(); 231 | } 232 | return map; 233 | } 234 | 235 | private String getMethodName(Method method) { 236 | String modifier = java.lang.reflect.Modifier.isPublic(method.getModifiers()) + "_" + java.lang.reflect.Modifier.isStatic(method.getModifiers()); 237 | return (method.getName() + "_" + modifier + "_" + method.getReturnType() + "_" + Arrays.toString(method.getParameterTypes())); 238 | } 239 | 240 | private void buildConstructorMethod(List list, Element element, Class clazz) { 241 | if (java.lang.reflect.Modifier.isAbstract(clazz.getModifiers())) { 242 | return; 243 | } 244 | Constructor[] constructors = clazz.getConstructors(); 245 | boolean existDefaultMethod = false; 246 | for (Constructor constructor : constructors) { 247 | if (java.lang.reflect.Modifier.isPublic(constructor.getModifiers())) { 248 | buildStaticConstructorMethod(list, element, constructor); 249 | MethodSpec.Builder builder = MethodSpec.constructorBuilder() 250 | .addModifiers(Modifier.PUBLIC); 251 | Class[] parameterTypes = constructor.getParameterTypes(); 252 | StringBuilder code = new StringBuilder(); 253 | code.append(ORIGIN_VALUE_NAME).append(" = new ").append(clazz.getSimpleName()).append("( "); 254 | if (parameterTypes.length == 0) { 255 | existDefaultMethod = true; 256 | } else { 257 | int i = 0; 258 | for (Class parameterType : parameterTypes) { 259 | String params = "arg" + (i++); 260 | builder.addParameter(parameterType, params); 261 | code.append(params).append(" ,"); 262 | } 263 | if (i > 0) { 264 | code.deleteCharAt(code.length() - 1); 265 | } 266 | } 267 | code.append(");\n"); 268 | builder.addCode(code.toString()); 269 | list.add(builder.build()); 270 | } 271 | } 272 | if (!existDefaultMethod) { 273 | list.add(MethodSpec.constructorBuilder() 274 | .addModifiers(Modifier.PUBLIC).build()); 275 | } 276 | } 277 | 278 | private void buildWrapperMethod(List list, Element element, Class clazz) { 279 | MethodSpec.Builder builder = MethodSpec.methodBuilder("wrapperInstance") 280 | .addModifiers(Modifier.PUBLIC, Modifier.STATIC) 281 | .addParameter(clazz, "arg0") 282 | .addJavadoc("包装value") 283 | .returns(Object.class); 284 | StringBuilder code = new StringBuilder(); 285 | code.append(element).append(" instance=new ").append(element).append("();\n "); 286 | code.append("instance.").append(ORIGIN_VALUE_NAME).append(" = arg0;\n "); 287 | code.append("return instance;\n "); 288 | builder.addCode(code.toString()); 289 | list.add(builder.build()); 290 | } 291 | 292 | 293 | private void buildStaticConstructorMethod(List list, Element element, Constructor constructor) { 294 | StringBuilder code = new StringBuilder(); 295 | code.append("return new ").append(element).append("( "); 296 | MethodSpec.Builder builder = MethodSpec.methodBuilder("newInstance") 297 | .addModifiers(Modifier.PUBLIC, Modifier.STATIC) 298 | .returns(Object.class); 299 | Class[] parameterTypes = constructor.getParameterTypes(); 300 | int i = 0; 301 | for (Class parameterType : parameterTypes) { 302 | String params = "arg" + (i++); 303 | builder.addParameter(parameterType, params); 304 | code.append(params).append(" ,"); 305 | } 306 | if (i > 0) { 307 | code.deleteCharAt(code.length() - 1); 308 | } 309 | code.append(");\n"); 310 | builder.addCode(code.toString()); 311 | list.add(builder.build()); 312 | } 313 | 314 | private void buildDefaultMethod(List list, JavaClass javaClass, Element element, Class clazz) { 315 | String jsName = javaClass.name(); 316 | if (jsName.isEmpty()) { 317 | jsName = element.getSimpleName().toString(); 318 | } 319 | MethodSpec.Builder getNameMethod = MethodSpec.methodBuilder("getJavaMethodName") 320 | .addAnnotation(Override.class) 321 | .addModifiers(Modifier.PUBLIC) 322 | .addCode("return \"" + jsName + "\";\n") 323 | .returns(String.class); 324 | MethodSpec.Builder getRealValueMethod = MethodSpec.methodBuilder("getRealValue") 325 | .addModifiers(Modifier.PUBLIC) 326 | .addJavadoc("获取原始值") 327 | .addCode("return " + ORIGIN_VALUE_NAME + ";\n") 328 | .returns(clazz); 329 | list.add(getNameMethod.build()); 330 | list.add(getRealValueMethod.build()); 331 | } 332 | 333 | private void buildMap() { 334 | if (javaMethodMap.isEmpty()) { 335 | return; 336 | } 337 | final String arrName = Constant.JAVA_METHOD_ARRAY_FIELD_NAME; 338 | MethodSpec.Builder builder = MethodSpec.methodBuilder(Constant.JAVA_METHOD_ARRAY_GET_METHOD_NAME) 339 | .addModifiers(Modifier.PUBLIC, Modifier.STATIC) 340 | .addCode("return " + arrName + ";\n") 341 | .returns(String[].class); 342 | StringBuilder staticCode = new StringBuilder(); 343 | staticCode.append(arrName + "=new String[").append(javaMethodMap.size()).append("]; \n "); 344 | int i = 0; 345 | for (Map.Entry entry : javaMethodMap.entrySet()) { 346 | staticCode.append(arrName).append("[").append(i++).append("]=\"").append(entry.getValue()).append("\";\n"); 347 | } 348 | TypeSpec typeSpec = TypeSpec.classBuilder(Constant.JAVA_METHOD_ARRAY_CLASS_NAME) 349 | .addMethods(Arrays.asList(builder.build())) 350 | .addField(String[].class, arrName, Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL) 351 | .addStaticBlock(CodeBlock.of(staticCode.toString())) 352 | .addModifiers(Modifier.PUBLIC, Modifier.FINAL).build(); 353 | buildFile(typeSpec); 354 | } 355 | 356 | private void buildJavaFile(String fileName, Class clazz, List list) { 357 | // 生成java文件,包含导航器Map 358 | TypeSpec typeSpec = TypeSpec.classBuilder(fileName + "_") 359 | .superclass(JavaMethod.class) 360 | .addSuperinterface(RealValueWrapper.class) 361 | .addMethods(list) 362 | .addField(clazz, ORIGIN_VALUE_NAME, Modifier.PROTECTED) 363 | .addModifiers(Modifier.PUBLIC).build(); 364 | buildFile(typeSpec); 365 | } 366 | 367 | private void buildFile(TypeSpec typeSpec) { 368 | if (moduleName == null) { 369 | mMessager.printMessage(Diagnostic.Kind.NOTE, "无法生成文件" + typeSpec.name + ",请在相应模块build.gradle中加入:\n" + 370 | "defaultConfig {\n javaCompileOptions {\n" + 371 | " annotationProcessorOptions {\n" + 372 | " includeCompileClasspath true\n" + 373 | " arguments = [moduleName: project.getName()]\n" + 374 | " }\n" + 375 | " }\n}"); 376 | return; 377 | } 378 | // 尝试生成文件 379 | try { 380 | JavaFile.builder(Constant.BUILD_PACKAGE + "." + moduleName, typeSpec) 381 | .build() 382 | .writeTo(mFiler); 383 | } catch (IOException e) { 384 | // e.printStackTrace(); 385 | } 386 | } 387 | } 388 | -------------------------------------------------------------------------------- /JsPosedCompile/src/main/java/com/wrbug/jsposedcompile/methodextension/CompoundButtonExtension.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposedcompile.methodextension; 2 | 3 | import android.widget.CompoundButton; 4 | 5 | import com.squareup.javapoet.MethodSpec; 6 | import com.wrbug.jsposedannotation.Constant; 7 | 8 | import org.mozilla.javascript.Function; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | import javax.lang.model.element.Modifier; 14 | 15 | public class CompoundButtonExtension implements MethodExtension { 16 | private static final String COMPOUND_BUTTON_CLASS_NAME = "android.widget.CompoundButton"; 17 | private static final String SET_ON_LONG_CHECKED_CHANGE_LISTENER_METHOD_NAME = "setOnCheckedChangeListener"; 18 | private static final String[] removeName = new String[]{SET_ON_LONG_CHECKED_CHANGE_LISTENER_METHOD_NAME}; 19 | 20 | @Override 21 | public boolean isSupport(Class target) { 22 | return CompoundButton.class.isAssignableFrom(target); 23 | } 24 | 25 | @Override 26 | public void build(List list) { 27 | removeMethod(list); 28 | list.add(buildOnCheckedChangeListener()); 29 | } 30 | 31 | private void removeMethod(List list) { 32 | List tmp = new ArrayList<>(); 33 | for (MethodSpec spec : list) { 34 | for (String s : removeName) { 35 | if (spec.name.equals(s)) { 36 | tmp.add(spec); 37 | } 38 | } 39 | } 40 | list.removeAll(tmp); 41 | } 42 | 43 | private MethodSpec buildOnCheckedChangeListener() { 44 | MethodSpec.Builder builder = MethodSpec.methodBuilder(SET_ON_LONG_CHECKED_CHANGE_LISTENER_METHOD_NAME) 45 | .addModifiers(Modifier.PUBLIC) 46 | .addParameter(Function.class, "function", Modifier.FINAL) 47 | .returns(void.class); 48 | String code = Constant.ORIGIN_VALUE_NAME + "." + SET_ON_LONG_CHECKED_CHANGE_LISTENER_METHOD_NAME + "(new " + COMPOUND_BUTTON_CLASS_NAME + ".OnCheckedChangeListener() {\n" + 49 | " @Override\n" + 50 | " public void onCheckedChanged(" + COMPOUND_BUTTON_CLASS_NAME + " buttonView, boolean isChecked) {\n" + 51 | " mJsPosedExecutor.call(function, buttonView, isChecked);\n" + 52 | " }\n" + 53 | " });"; 54 | builder.addCode(code); 55 | return builder.build(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /JsPosedCompile/src/main/java/com/wrbug/jsposedcompile/methodextension/MethodExtension.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposedcompile.methodextension; 2 | 3 | import com.squareup.javapoet.MethodSpec; 4 | 5 | import java.util.List; 6 | 7 | public interface MethodExtension { 8 | boolean isSupport(Class target); 9 | 10 | void build(List list); 11 | } 12 | -------------------------------------------------------------------------------- /JsPosedCompile/src/main/java/com/wrbug/jsposedcompile/methodextension/MethodExtensionManager.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposedcompile.methodextension; 2 | 3 | import com.squareup.javapoet.MethodSpec; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | import javax.annotation.processing.Messager; 9 | import javax.tools.Diagnostic; 10 | 11 | public class MethodExtensionManager { 12 | private List mMethodExtensionList; 13 | private Messager mMessager; 14 | 15 | public MethodExtensionManager(Messager messager) { 16 | mMessager = messager; 17 | mMethodExtensionList = new ArrayList<>(); 18 | mMethodExtensionList.add(new ViewExtension()); 19 | mMethodExtensionList.add(new CompoundButtonExtension()); 20 | mMethodExtensionList.add(new XposedHelpersExtension()); 21 | mMethodExtensionList.add(new RealValueWrapperExtension(messager)); 22 | } 23 | 24 | public List getExtension(Class target) { 25 | List list = new ArrayList<>(); 26 | for (MethodExtension methodExtension : mMethodExtensionList) { 27 | if (methodExtension.isSupport(target)) { 28 | // mMessager.printMessage(Diagnostic.Kind.NOTE, target.getName() + " support " + methodExtension); 29 | list.add(methodExtension); 30 | } 31 | } 32 | return list; 33 | } 34 | 35 | public void build(List list, Class target) { 36 | List extension = getExtension(target); 37 | for (MethodExtension methodExtension : extension) { 38 | methodExtension.build(list); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /JsPosedCompile/src/main/java/com/wrbug/jsposedcompile/methodextension/RealValueWrapperExtension.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposedcompile.methodextension; 2 | 3 | import com.squareup.javapoet.CodeBlock; 4 | import com.squareup.javapoet.MethodSpec; 5 | import com.squareup.javapoet.ParameterSpec; 6 | import com.squareup.javapoet.TypeName; 7 | import com.wrbug.jsposedannotation.RealValueWrapper; 8 | import com.wrbug.jsposedcompile.DefaultValue; 9 | import com.wrbug.jsposedcompile.JavaClassListUtils; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | import javax.annotation.processing.Messager; 16 | import javax.lang.model.element.Modifier; 17 | import javax.tools.Diagnostic; 18 | 19 | public class RealValueWrapperExtension implements MethodExtension { 20 | private Messager mMessager; 21 | private static final String RealValueWrapperClassName = RealValueWrapper.class.getName(); 22 | 23 | public RealValueWrapperExtension(Messager messager) { 24 | mMessager = messager; 25 | } 26 | 27 | @Override 28 | public boolean isSupport(Class target) { 29 | return true; 30 | } 31 | 32 | @Override 33 | public void build(List list) { 34 | try { 35 | Map map = JavaClassListUtils.getList(); 36 | List newMethod = new ArrayList<>(); 37 | List existMethod = new ArrayList<>(); 38 | for (MethodSpec methodSpec : list) { 39 | if (methodSpec.isConstructor()) { 40 | continue; 41 | } 42 | boolean changed = false; 43 | MethodSpec.Builder builder = toBuilder(methodSpec); 44 | StringBuilder defaultValueCode = new StringBuilder(); 45 | StringBuilder code = new StringBuilder(); 46 | if (!methodSpec.returnType.toString().equals(void.class.getName())) { 47 | code.append("return "); 48 | } 49 | code.append(methodSpec.name).append("("); 50 | for (int i = 0; i < methodSpec.parameters.size(); i++) { 51 | ParameterSpec parameter = methodSpec.parameters.get(i); 52 | String key = parameter.type.toString(); 53 | if (map.containsKey(key)) { 54 | Modifier[] modifiers = new Modifier[parameter.modifiers.size()]; 55 | parameter.modifiers.toArray(modifiers); 56 | parameter = ParameterSpec.builder(RealValueWrapper.class, parameter.name, modifiers).build(); 57 | code.append("(").append(key).append(")").append(parameter.name).append(".").append("getRealValue(),"); 58 | defaultValueCode.append(getDefaultValueCode(methodSpec.returnType, key, parameter.name)); 59 | changed = true; 60 | } else { 61 | code.append(parameter.name).append(","); 62 | } 63 | builder.addParameter(parameter); 64 | } 65 | if (changed) { 66 | code.deleteCharAt(code.length() - 1); 67 | code.append(");\n"); 68 | builder.addCode(defaultValueCode.toString() + code.toString()); 69 | MethodSpec spec = builder.build(); 70 | String methodSpecString = getMethodSpecString(spec); 71 | if (existMethod.contains(methodSpecString)) { 72 | mMessager.printMessage(Diagnostic.Kind.NOTE, "忽略" + spec.name + spec.parameters); 73 | continue; 74 | } 75 | mMessager.printMessage(Diagnostic.Kind.NOTE, "add methodSpecString: " + methodSpecString); 76 | existMethod.add(methodSpecString); 77 | newMethod.add(spec); 78 | } 79 | } 80 | mMessager.printMessage(Diagnostic.Kind.NOTE, "newMethod size=" + newMethod.size()); 81 | list.addAll(newMethod); 82 | } catch (Throwable throwable) { 83 | throwable.printStackTrace(); 84 | } 85 | } 86 | 87 | 88 | private String getMethodSpecString(MethodSpec methodSpec) { 89 | List typeNames = new ArrayList<>(); 90 | for (ParameterSpec parameter : methodSpec.parameters) { 91 | typeNames.add(parameter.type); 92 | } 93 | return methodSpec.name + typeNames + methodSpec.returnType; 94 | } 95 | 96 | private String getDefaultValueCode(TypeName returnType, String instanceOfClassName, String name) { 97 | StringBuilder defaultCode = new StringBuilder(); 98 | String s = DefaultValue.get(returnType); 99 | defaultCode.append("if(!(").append(name).append(".getRealValue() instanceof ").append(instanceOfClassName).append(")){\n return ").append(s).append(";\n}\n"); 100 | return defaultCode.toString(); 101 | } 102 | 103 | private MethodSpec.Builder toBuilder(MethodSpec methodSpec) { 104 | MethodSpec.Builder builder = MethodSpec.methodBuilder(methodSpec.name); 105 | CodeBlock defaultValue = methodSpec.defaultValue; 106 | builder.addJavadoc("RealValueWrapperExtension 自动生成,方便用于js调用") 107 | .addAnnotations(methodSpec.annotations) 108 | .addModifiers(methodSpec.modifiers) 109 | .addTypeVariables(methodSpec.typeVariables) 110 | .returns(methodSpec.returnType) 111 | .addExceptions(methodSpec.exceptions) 112 | .varargs(methodSpec.varargs); 113 | if (defaultValue != null) { 114 | builder.defaultValue(defaultValue); 115 | } 116 | return builder; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /JsPosedCompile/src/main/java/com/wrbug/jsposedcompile/methodextension/ViewExtension.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposedcompile.methodextension; 2 | 3 | import android.view.View; 4 | 5 | import com.squareup.javapoet.MethodSpec; 6 | import com.wrbug.jsposedannotation.Constant; 7 | 8 | import org.mozilla.javascript.Function; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | import javax.lang.model.element.Modifier; 14 | 15 | public class ViewExtension implements MethodExtension { 16 | private static final String VIEW_CLASS_NAME = "android.view.View"; 17 | private static final String SET_ON_LONG_CLICK_LISTENER_METHOD_NAME = "setOnLongClickListener"; 18 | private static final String SET_ON_FOCUS_CHANGE_LISTENER_METHOD_NAME = "setOnFocusChangeListener"; 19 | private static final String SET_ON_CLICK_LISTENER_METHOD_NAME = "setOnClickListener"; 20 | private static final String[] removeName = new String[]{SET_ON_LONG_CLICK_LISTENER_METHOD_NAME, SET_ON_FOCUS_CHANGE_LISTENER_METHOD_NAME, SET_ON_CLICK_LISTENER_METHOD_NAME}; 21 | 22 | @Override 23 | public boolean isSupport(Class target) { 24 | return View.class.isAssignableFrom(target); 25 | } 26 | 27 | @Override 28 | public void build(List list) { 29 | removeMethod(list); 30 | list.add(buildOnclickListener()); 31 | list.add(buildOnFocusChangeListener()); 32 | list.add(buildOnLongClickListener()); 33 | } 34 | 35 | private void removeMethod(List list) { 36 | List tmp = new ArrayList<>(); 37 | for (MethodSpec spec : list) { 38 | for (String s : removeName) { 39 | if (spec.name.equals(s)) { 40 | tmp.add(spec); 41 | } 42 | } 43 | } 44 | list.removeAll(tmp); 45 | } 46 | 47 | private MethodSpec buildOnLongClickListener() { 48 | MethodSpec.Builder builder = MethodSpec.methodBuilder(SET_ON_LONG_CLICK_LISTENER_METHOD_NAME) 49 | .addModifiers(Modifier.PUBLIC) 50 | .addParameter(Function.class, "function", Modifier.FINAL) 51 | .returns(void.class); 52 | String code = Constant.ORIGIN_VALUE_NAME + "." + SET_ON_LONG_CLICK_LISTENER_METHOD_NAME + "(new " + VIEW_CLASS_NAME + ".OnLongClickListener() {\n" + 53 | " @Override\n" + 54 | " public boolean onLongClick(" + VIEW_CLASS_NAME + " v) {\n" + 55 | " mJsPosedExecutor.call(function, v);\n" + 56 | " return false;\n" + 57 | " }\n" + 58 | " });"; 59 | builder.addCode(code); 60 | return builder.build(); 61 | } 62 | 63 | private MethodSpec buildOnFocusChangeListener() { 64 | MethodSpec.Builder builder = MethodSpec.methodBuilder(SET_ON_FOCUS_CHANGE_LISTENER_METHOD_NAME) 65 | .addModifiers(Modifier.PUBLIC) 66 | .addParameter(Function.class, "function", Modifier.FINAL) 67 | .returns(void.class); 68 | String code = Constant.ORIGIN_VALUE_NAME + "." + SET_ON_FOCUS_CHANGE_LISTENER_METHOD_NAME + "(new " + VIEW_CLASS_NAME + ".OnFocusChangeListener() {\n" + 69 | " @Override\n" + 70 | " public void onFocusChange(" + VIEW_CLASS_NAME + " v, boolean hasFocus) {\n" + 71 | " mJsPosedExecutor.call(function, v, hasFocus);\n" + 72 | " }\n" + 73 | " });"; 74 | builder.addCode(code); 75 | return builder.build(); 76 | } 77 | 78 | private MethodSpec buildOnclickListener() { 79 | MethodSpec.Builder builder = MethodSpec.methodBuilder(SET_ON_CLICK_LISTENER_METHOD_NAME) 80 | .addModifiers(Modifier.PUBLIC) 81 | .addParameter(Function.class, "function", Modifier.FINAL) 82 | .returns(void.class); 83 | String code = Constant.ORIGIN_VALUE_NAME + "." + SET_ON_CLICK_LISTENER_METHOD_NAME + "(new " + VIEW_CLASS_NAME + ".OnClickListener() {\n" + 84 | " @Override\n" + 85 | " public void onClick(" + VIEW_CLASS_NAME + " v) {\n" + 86 | " mJsPosedExecutor.call(function, v);\n" + 87 | " }\n" + 88 | " });"; 89 | builder.addCode(code); 90 | return builder.build(); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /JsPosedCompile/src/main/java/com/wrbug/jsposedcompile/methodextension/XposedHelpersExtension.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposedcompile.methodextension; 2 | 3 | import com.squareup.javapoet.MethodSpec; 4 | 5 | import org.mozilla.javascript.Function; 6 | 7 | import java.util.List; 8 | 9 | import javax.lang.model.element.Modifier; 10 | 11 | import de.robv.android.xposed.XposedHelpers; 12 | 13 | public class XposedHelpersExtension implements MethodExtension { 14 | private static final String XPOSED_HELPERS_CLASS_NAME = "de.robv.android.xposed.XposedHelpers"; 15 | 16 | @Override 17 | public boolean isSupport(Class target) { 18 | return XposedHelpers.class.isAssignableFrom(target); 19 | } 20 | 21 | @Override 22 | public void build(List list) { 23 | // list.add(buildFindClassMethod()); 24 | } 25 | 26 | 27 | private MethodSpec buildFindClassMethod() { 28 | MethodSpec.Builder builder = MethodSpec.methodBuilder("findClass") 29 | .addModifiers(Modifier.PUBLIC) 30 | .addJavadoc(getClass().getSimpleName() + " 自动生成") 31 | .addParameter(String.class, "className", Modifier.FINAL) 32 | .addCode("return findClassIfExists(className, mParam.classLoader);") 33 | .returns(Class.class); 34 | return builder.build(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 WrBug 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | 5 | repositories { 6 | maven { 7 | url uri('repo') //插件所在的目录 8 | } 9 | google() 10 | jcenter() 11 | maven{ 12 | url "https://dl.bintray.com/wrbug/maven/" 13 | } 14 | 15 | } 16 | dependencies { 17 | classpath 'com.android.tools.build:gradle:3.2.1' 18 | classpath 'com.wrbug.jsposedgradleplugin:jsposedgradleplugin:1.0.0' 19 | 20 | // NOTE: Do not place your application dependencies here; they belong 21 | // in the individual module build.gradle files 22 | } 23 | } 24 | 25 | allprojects { 26 | repositories { 27 | google() 28 | jcenter() 29 | maven{ 30 | url "https://dl.bintray.com/wrbug/maven/" 31 | } 32 | } 33 | } 34 | 35 | task clean(type: Delete) { 36 | delete rootProject.buildDir 37 | } 38 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | 15 | 16 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WrBug/JsPosed/ce9569f3e2764ea7cf18351eaf6c1914c375942a/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-4.6-all.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 | -------------------------------------------------------------------------------- /jsposed/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /jsposed/bintray.gradle: -------------------------------------------------------------------------------- 1 | group = PROJ_GROUP 2 | version = PROJ_VERSION 3 | project.archivesBaseName = PROJ_ARTIFACTID 4 | 5 | apply plugin: 'com.jfrog.bintray' 6 | apply plugin: 'com.github.dcendents.android-maven' 7 | 8 | task sourcesJar(type: Jar) { 9 | from android.sourceSets.main.java.srcDirs 10 | classifier = 'sources' 11 | } 12 | 13 | task javadoc(type: Javadoc) { 14 | source = android.sourceSets.main.java.srcDirs 15 | classpath += configurations.compile 16 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) 17 | } 18 | 19 | task javadocJar(type: Jar, dependsOn: javadoc) { 20 | classifier = 'javadoc' 21 | from javadoc.destinationDir 22 | } 23 | 24 | task syncJavadoc(type: Sync, dependsOn: javadoc) { 25 | from javadoc.destinationDir 26 | into rootProject.file('docs') 27 | } 28 | 29 | javadoc { 30 | options{ 31 | encoding "UTF-8" 32 | charSet 'UTF-8' 33 | author true 34 | version true 35 | title "$PROJ_NAME $PROJ_VERSION" 36 | String packageListRef = "${android.sdkDirectory}/docs/reference/" 37 | linksOffline 'http://d.android.com/reference/', packageListRef 38 | } 39 | } 40 | 41 | artifacts { 42 | archives sourcesJar 43 | archives javadocJar 44 | } 45 | 46 | install { 47 | repositories.mavenInstaller { 48 | pom.project { 49 | name PROJ_NAME 50 | description PROJ_DESCRIPTION 51 | url PROJ_WEBSITEURL 52 | inceptionYear '2016' 53 | 54 | packaging 'aar' 55 | groupId PROJ_GROUP 56 | artifactId PROJ_ARTIFACTID 57 | version PROJ_VERSION 58 | 59 | licenses { 60 | license { 61 | name 'The Apache Software License, Version 2.0' 62 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt' 63 | distribution 'repo' 64 | } 65 | } 66 | scm { 67 | connection PROJ_VCSURL 68 | url PROJ_WEBSITEURL 69 | 70 | } 71 | developers { 72 | developer { 73 | id DEVELOPER_ID 74 | name DEVELOPER_NAME 75 | email DEVELOPER_EMAIL 76 | } 77 | } 78 | } 79 | } 80 | } 81 | 82 | bintray { 83 | user = project.hasProperty('bintrayUser') ? project.property('bintrayUser') : getLocalProperties('BINTRAY_USER') 84 | key = project.hasProperty('bintrayKey') ? project.property('bintrayKey') : getLocalProperties('BINTRAY_KEY') 85 | 86 | configurations = ['archives'] 87 | 88 | dryRun = false 89 | publish = true 90 | 91 | pkg { 92 | repo = 'maven' 93 | name = PROJ_NAME 94 | licenses = ['Apache-2.0'] 95 | vcsUrl = PROJ_VCSURL 96 | websiteUrl = PROJ_WEBSITEURL 97 | issueTrackerUrl = PROJ_ISSUETRACKERURL 98 | publicDownloadNumbers = true 99 | version { 100 | name = PROJ_VERSION 101 | desc = PROJ_DESCRIPTION 102 | vcsTag = PROJ_VERSION 103 | 104 | gpg { 105 | sign = true 106 | } 107 | } 108 | } 109 | } 110 | 111 | 112 | def getLocalProperties(key){ 113 | File file = rootProject.file('local.properties') 114 | if(file.exists()){ 115 | InputStream inputStream = rootProject.file('local.properties').newDataInputStream(); 116 | Properties properties = new Properties() 117 | properties.load(inputStream) 118 | 119 | if(properties.containsKey(key)){ 120 | return properties.getProperty(key) 121 | } 122 | } 123 | return "" 124 | } -------------------------------------------------------------------------------- /jsposed/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 28 5 | 6 | lintOptions { 7 | abortOnError false 8 | } 9 | 10 | defaultConfig { 11 | minSdkVersion 21 12 | targetSdkVersion 28 13 | versionCode 2 14 | versionName PROJ_VERSION 15 | 16 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 17 | javaCompileOptions { 18 | annotationProcessorOptions { 19 | includeCompileClasspath true 20 | arguments = [moduleName: project.getName()] 21 | } 22 | } 23 | 24 | 25 | } 26 | 27 | buildTypes { 28 | release { 29 | minifyEnabled false 30 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 31 | } 32 | } 33 | } 34 | 35 | dependencies { 36 | implementation fileTree(include: ['*.jar'], dir: 'libs') 37 | implementation 'com.android.support:appcompat-v7:28.0.0' 38 | testImplementation 'junit:junit:4.12' 39 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 40 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 41 | // https://mvnrepository.com/artifact/org.mozilla/rhino 42 | implementation 'org.mozilla:rhino:1.7.10' 43 | compileOnly 'de.robv.android.xposed:api:82' 44 | implementation project(':JsPosedAnnotation') 45 | annotationProcessor project(':JsPosedCompile') 46 | } 47 | 48 | 49 | buildscript { 50 | repositories { 51 | jcenter() 52 | google() 53 | } 54 | dependencies { 55 | classpath 'com.github.dcendents:android-maven-gradle-plugin:2.0' 56 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4' 57 | } 58 | } 59 | 60 | // This script was used to upload files to bintray. 61 | // Note: please put it on the bottm. 62 | apply from: 'bintray.gradle' -------------------------------------------------------------------------------- /jsposed/gradle.properties: -------------------------------------------------------------------------------- 1 | PROJ_GROUP=com.wrbug 2 | PROJ_VERSION_CODE=2 3 | PROJ_VERSION=0.0.2 4 | PROJ_NAME=JsPosed 5 | PROJ_WEBSITEURL=https://github.com/WrBug/JsPosed 6 | PROJ_ISSUETRACKERURL=https://github.com/WrBug/JsPosed 7 | PROJ_VCSURL=git@github.com:WrBug/JsPosed.git 8 | PROJ_DESCRIPTION= 9 | PROJ_ARTIFACTID=jsposed 10 | 11 | DEVELOPER_ID= 12 | DEVELOPER_NAME= wrbug 13 | DEVELOPER_EMAIL= iwrbug@gmail.com -------------------------------------------------------------------------------- /jsposed/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /jsposed/src/androidTest/java/com/wrbug/jsposed/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposed; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.wrbug.jsposed.test", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /jsposed/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /jsposed/src/main/java/com/wrbug/jsposed/ArrayManager.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposed; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | 7 | public class ArrayManager { 8 | private List mList = new ArrayList(); 9 | 10 | public static ArrayManager getInstance() { 11 | ArrayManager manager = new ArrayManager(); 12 | return manager; 13 | } 14 | 15 | public ArrayManager addList(List list) { 16 | mList.addAll(list); 17 | return this; 18 | } 19 | 20 | public ArrayManager addArray(Object[] data) { 21 | mList.addAll(Arrays.asList(data)); 22 | return this; 23 | } 24 | 25 | public ArrayManager add(Object data) { 26 | mList.add(data); 27 | return this; 28 | } 29 | 30 | public Object[] toArray() { 31 | return mList.toArray(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /jsposed/src/main/java/com/wrbug/jsposed/ClassUtils.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposed; 2 | 3 | public class ClassUtils { 4 | 5 | public static Class[] toClass(ClassLoader classLoader, Object[] clzs) { 6 | if (clzs == null) { 7 | return null; 8 | } 9 | Class[] arr = new Class[clzs.length]; 10 | for (int i = 0; i < clzs.length; i++) { 11 | Class c = null; 12 | Object clz = clzs[i]; 13 | if (clz instanceof String) { 14 | c = checkIsPrimitive(clz); 15 | if (c == null) { 16 | try { 17 | c = classLoader.loadClass((String) clz); 18 | } catch (ClassNotFoundException e) { 19 | e.printStackTrace(); 20 | } 21 | } 22 | } else if (clz instanceof Class) { 23 | c = (Class) clz; 24 | } 25 | arr[i] = c; 26 | } 27 | return arr; 28 | } 29 | 30 | public static Class toClass(Object obj, ClassLoader classLoader) { 31 | if (obj == null) { 32 | return null; 33 | } 34 | Class c = null; 35 | if (obj instanceof String) { 36 | c = checkIsPrimitive(obj); 37 | if (c == null) { 38 | try { 39 | c = classLoader.loadClass((String) obj); 40 | } catch (ClassNotFoundException e) { 41 | e.printStackTrace(); 42 | } 43 | } 44 | } else if (obj instanceof Class) { 45 | c = (Class) obj; 46 | } 47 | return c; 48 | } 49 | 50 | public static Class toClass(Object obj) { 51 | if (obj == null) { 52 | return null; 53 | } 54 | Class c = null; 55 | if (obj instanceof String) { 56 | c = checkIsPrimitive(obj); 57 | if (c == null) { 58 | try { 59 | c = Class.forName((String) obj); 60 | } catch (ClassNotFoundException e) { 61 | e.printStackTrace(); 62 | } 63 | } 64 | } else if (obj instanceof Class) { 65 | c = (Class) obj; 66 | } 67 | return c; 68 | } 69 | 70 | public static Class[] toClass(Object[] clzs) { 71 | if (clzs == null) { 72 | return null; 73 | } 74 | Class[] arr = new Class[clzs.length]; 75 | for (int i = 0; i < clzs.length; i++) { 76 | Object clz = clzs[i]; 77 | arr[i] = toClass(clz); 78 | } 79 | return arr; 80 | } 81 | 82 | private static Class checkIsPrimitive(Object o) { 83 | if (!(o instanceof String)) { 84 | return null; 85 | } 86 | String type = ((String) o).toLowerCase(); 87 | switch (type) { 88 | case "int": { 89 | return int.class; 90 | } 91 | case "long": { 92 | return long.class; 93 | } 94 | case "float": { 95 | return float.class; 96 | } 97 | case "double": { 98 | return double.class; 99 | } 100 | case "char": { 101 | return char.class; 102 | } 103 | case "short": { 104 | return short.class; 105 | } 106 | case "byte": { 107 | return byte.class; 108 | } 109 | case "boolean": { 110 | return boolean.class; 111 | } 112 | } 113 | return null; 114 | } 115 | 116 | public static Object[] convertObject(Class[] classes, Object[] args) { 117 | if (classes == null || args == null) { 118 | return null; 119 | } 120 | if (classes.length != args.length) { 121 | return null; 122 | } 123 | for (int i = 0; i < args.length; i++) { 124 | if (args[i] instanceof Number) { 125 | args[i] = JsNumberUtils.convertNumber(classes[i], ((Number) args[i])); 126 | } else if (args[i] instanceof String && isNumberClass(classes[i])) { 127 | args[i] = JsNumberUtils.convertNumber(classes[i], ((String) args[i])); 128 | } 129 | } 130 | return args; 131 | } 132 | 133 | public static boolean isNumberClass(Class clazz) { 134 | return Number.class.isAssignableFrom(clazz) || 135 | clazz == int.class || 136 | clazz == long.class || 137 | clazz == float.class || 138 | clazz == double.class || 139 | clazz == byte.class || 140 | clazz == short.class; 141 | } 142 | 143 | } 144 | -------------------------------------------------------------------------------- /jsposed/src/main/java/com/wrbug/jsposed/CommonMethodHook.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposed; 2 | 3 | import com.wrbug.jsposedannotation.Executable; 4 | 5 | import org.mozilla.javascript.Function; 6 | 7 | import de.robv.android.xposed.XC_MethodHook; 8 | 9 | import static com.wrbug.jsposed.LogUtils.log; 10 | 11 | public class CommonMethodHook extends XC_MethodHook { 12 | private Executable mJsPosedExecutor; 13 | private Function beforeCall; 14 | private Function afterCall; 15 | 16 | public CommonMethodHook(Executable jsPosedExecutor, Function beforeCall, Function afterCall) { 17 | mJsPosedExecutor = jsPosedExecutor; 18 | this.beforeCall = beforeCall; 19 | this.afterCall = afterCall; 20 | } 21 | 22 | @Override 23 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable { 24 | log("beforeHookedMethod" + param.method); 25 | mJsPosedExecutor.call(beforeCall, param); 26 | super.beforeHookedMethod(param); 27 | } 28 | 29 | @Override 30 | protected void afterHookedMethod(MethodHookParam param) throws Throwable { 31 | log("afterHookedMethod" + param.method); 32 | mJsPosedExecutor.call(afterCall, param); 33 | super.afterHookedMethod(param); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /jsposed/src/main/java/com/wrbug/jsposed/JaveMethodWapper.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposed; 2 | 3 | import com.wrbug.jsposedannotation.Executable; 4 | import com.wrbug.jsposedannotation.JavaMethod; 5 | 6 | import de.robv.android.xposed.callbacks.XC_LoadPackage; 7 | 8 | public class JaveMethodWapper extends JavaMethod { 9 | private Class clazz; 10 | private Object mBase; 11 | 12 | public JaveMethodWapper(Object base) { 13 | mBase = base; 14 | clazz = base.getClass(); 15 | } 16 | 17 | 18 | } 19 | -------------------------------------------------------------------------------- /jsposed/src/main/java/com/wrbug/jsposed/JsNumberUtils.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposed; 2 | 3 | import android.text.TextUtils; 4 | 5 | import java.math.BigDecimal; 6 | import java.math.BigInteger; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | import java.util.regex.Pattern; 10 | 11 | 12 | /** 13 | * 14 | * 15 | */ 16 | public class JsNumberUtils { 17 | private static Map sNumberMap = new HashMap<>(); 18 | 19 | public static boolean isInt(String str) { 20 | if (!isNumber(str)) { 21 | return false; 22 | } 23 | long value = toLong(str); 24 | return value <= Integer.MAX_VALUE && value >= Integer.MIN_VALUE; 25 | } 26 | 27 | /** 28 | * 1234L or 1234l 29 | * 30 | * @param str 31 | * @return 32 | */ 33 | public static boolean isLong(String str) { 34 | if (TextUtils.isEmpty(str)) { 35 | return false; 36 | } 37 | if (!str.endsWith("L") && !str.endsWith("l")) { 38 | return false; 39 | } 40 | str = str.substring(0, str.length() - 1); 41 | if (!isNumber(str)) { 42 | return false; 43 | } 44 | double value = toDouble(str); 45 | return value <= Long.MAX_VALUE && value >= Long.MIN_VALUE; 46 | } 47 | 48 | /** 49 | * 1234F or 1234.1f 50 | * 51 | * @param str 52 | * @return 53 | */ 54 | public static boolean isFloat(String str) { 55 | if (TextUtils.isEmpty(str)) { 56 | return false; 57 | } 58 | if (!str.endsWith("F") && !str.endsWith("f")) { 59 | return false; 60 | } 61 | str = str.substring(0, str.length() - 1); 62 | if (!isDecimal(str)) { 63 | return false; 64 | } 65 | double value = toDouble(str); 66 | return value <= Float.MAX_VALUE && value >= Float.MIN_VALUE; 67 | } 68 | 69 | public static boolean isDouble(String str) { 70 | return isDecimal(str); 71 | } 72 | 73 | public static boolean isNumber(String str) { 74 | if (TextUtils.isEmpty(str)) { 75 | return false; 76 | } 77 | if (str.equals("0")) { 78 | return true; 79 | } 80 | Pattern pattern = Pattern.compile("-?[1-9][0-9]*$"); 81 | return pattern.matcher(str).find(); 82 | } 83 | 84 | public static boolean isDecimal(String str) { 85 | if (TextUtils.isEmpty(str)) { 86 | return false; 87 | } 88 | if (str.equals("0")) { 89 | return true; 90 | } 91 | Pattern pattern = Pattern.compile("-?([0-9]+\\.0*)?[1-9][0-9]*$"); 92 | return pattern.matcher(str).find(); 93 | } 94 | 95 | public static boolean isHex(String str) { 96 | if (TextUtils.isEmpty(str)) { 97 | return false; 98 | } 99 | Pattern pattern = Pattern.compile("0x[0-9a-fA-F]*$"); 100 | return pattern.matcher(str).find(); 101 | } 102 | 103 | public static Number toNumber(String str) { 104 | if (sNumberMap.containsKey(str)) { 105 | return sNumberMap.get(str); 106 | } 107 | if (isHex(str)) { 108 | BigInteger number = new BigInteger(str.substring(2), 16); 109 | sNumberMap.put(str, number); 110 | return number; 111 | } else if (isFloat(str) || isLong(str) || isDecimal(str)) { 112 | String num = str.replace("F", "").replace("f", "") 113 | .replace("L", "").replace("l", ""); 114 | BigDecimal bigDecimal = new BigDecimal(num); 115 | sNumberMap.put(str, bigDecimal); 116 | return bigDecimal; 117 | } 118 | return 0; 119 | } 120 | 121 | public static int toInt(String str) { 122 | return toNumber(str).intValue(); 123 | } 124 | 125 | public static long toLong(String str) { 126 | return toNumber(str).longValue(); 127 | } 128 | 129 | public static float toFloat(String str) { 130 | return toNumber(str).floatValue(); 131 | } 132 | 133 | public static double toDouble(String str) { 134 | return toNumber(str).doubleValue(); 135 | } 136 | 137 | public static Number toNumber(Object data) { 138 | if (data instanceof Number) { 139 | return (Number) data; 140 | } else if (data instanceof String) { 141 | return toNumber((String) data); 142 | } 143 | return 0; 144 | } 145 | 146 | public static Number convertNumber(Class clazz, String data) { 147 | Number number = null; 148 | if (JsNumberUtils.isHex(data)) { 149 | number = new BigInteger(data.substring(2), 16); 150 | } else if (isFloat(data) || isLong(data) || isDecimal(data)) { 151 | String num = data.replace("F", "").replace("f", "") 152 | .replace("L", "").replace("l", ""); 153 | number = new BigDecimal(num); 154 | } 155 | return convertNumber(clazz, number); 156 | } 157 | 158 | public static Number convertNumber(Class clazz, Number data) { 159 | if (clazz == null || data == null) { 160 | return 0; 161 | } 162 | if (clazz == Integer.class || clazz == int.class) { 163 | return data.intValue(); 164 | } 165 | if (clazz == Long.class || clazz == long.class) { 166 | return data.longValue(); 167 | } 168 | if (clazz == Float.class || clazz == float.class) { 169 | return data.floatValue(); 170 | } 171 | if (clazz == Double.class || clazz == double.class) { 172 | return data.doubleValue(); 173 | } 174 | if (clazz == Byte.class || clazz == byte.class) { 175 | return data.byteValue(); 176 | } 177 | if (clazz == Short.class || clazz == short.class) { 178 | return data.shortValue(); 179 | } 180 | return 0; 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /jsposed/src/main/java/com/wrbug/jsposed/JsPosedConfig.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposed; 2 | 3 | class JsPosedConfig { 4 | public static boolean debug; 5 | } 6 | -------------------------------------------------------------------------------- /jsposed/src/main/java/com/wrbug/jsposed/JsPosedExecutor.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposed; 2 | 3 | import android.text.TextUtils; 4 | 5 | import com.wrbug.jsposed.jscall.xposed.Env; 6 | import com.wrbug.jsposed.jscall.xposed.JsPosedBridge; 7 | import com.wrbug.jsposedannotation.Constant; 8 | import com.wrbug.jsposedannotation.Executable; 9 | import com.wrbug.jsposedannotation.JavaMethod; 10 | 11 | import org.mozilla.javascript.Context; 12 | import org.mozilla.javascript.Function; 13 | import org.mozilla.javascript.Scriptable; 14 | import org.mozilla.javascript.ScriptableObject; 15 | 16 | import java.lang.reflect.Constructor; 17 | import java.lang.reflect.Field; 18 | import java.lang.reflect.Method; 19 | import java.util.ArrayList; 20 | import java.util.Arrays; 21 | import java.util.List; 22 | 23 | import de.robv.android.xposed.XC_MethodHook; 24 | import de.robv.android.xposed.XposedBridge; 25 | import de.robv.android.xposed.XposedHelpers; 26 | import de.robv.android.xposed.callbacks.XC_LoadPackage; 27 | 28 | public class JsPosedExecutor implements Executable { 29 | private String js; 30 | private XC_LoadPackage.LoadPackageParam mParam; 31 | private volatile static JsPosedExecutor instance; 32 | private Context mContext; 33 | private Scriptable scope; 34 | private List javaMethods = new ArrayList<>(); 35 | private static final String TAG = "JsPosedExecutor"; 36 | 37 | 38 | private static void log(Object... obj) { 39 | if (obj == null || obj.length == 0) { 40 | return; 41 | } 42 | XposedBridge.log("------" + TAG + "------"); 43 | for (Object o : obj) { 44 | if (o instanceof Throwable) { 45 | XposedBridge.log(((Throwable) o)); 46 | } else { 47 | XposedBridge.log(String.valueOf(o)); 48 | } 49 | } 50 | XposedBridge.log("------------\n"); 51 | } 52 | 53 | public static JsPosedExecutor init(XC_LoadPackage.LoadPackageParam param, String jsContent) { 54 | return init(param, jsContent, false); 55 | } 56 | 57 | public static JsPosedExecutor init(XC_LoadPackage.LoadPackageParam param, String jsContent, boolean debug) { 58 | JsPosedConfig.debug = debug; 59 | if (instance == null) { 60 | synchronized (JsPosedExecutor.class) { 61 | if (instance == null) { 62 | instance = new JsPosedExecutor(param, jsContent); 63 | } 64 | } 65 | } 66 | instance.checkJsChanged(jsContent); 67 | return instance; 68 | } 69 | 70 | private JsPosedExecutor(XC_LoadPackage.LoadPackageParam param, String js) { 71 | initCallLog(); 72 | this.mParam = param; 73 | this.js = js; 74 | mContext = Context.enter(); 75 | mContext.setOptimizationLevel(-1); 76 | scope = mContext.initSafeStandardObjects(); 77 | loadJavaClass(); 78 | addJavaMethod(new JsPosedBridge()); 79 | addJavaMethod(new Env()); 80 | run(js); 81 | } 82 | 83 | private void loadJavaClass() { 84 | Class buildConfigClass = XposedHelpers.findClassIfExists(mParam.packageName + ".BuildConfig", mParam.classLoader); 85 | if (buildConfigClass == null) { 86 | return; 87 | } 88 | Field field = null; 89 | try { 90 | field = buildConfigClass.getDeclaredField("MODULES_NAME"); 91 | } catch (NoSuchFieldException e) { 92 | } 93 | if (field == null) { 94 | return; 95 | } 96 | String modulesName = (String) XposedHelpers.getStaticObjectField(buildConfigClass, "MODULES_NAME"); 97 | log("modulesName=" + modulesName); 98 | if (TextUtils.isEmpty(modulesName)) { 99 | return; 100 | } 101 | String[] modulesNames = modulesName.split(","); 102 | if (modulesNames.length > 0) { 103 | for (String name : modulesNames) { 104 | String className = Constant.BUILD_PACKAGE + "." + name + "." + Constant.JAVA_METHOD_ARRAY_CLASS_NAME; 105 | Class javaMethodMapClass = XposedHelpers.findClassIfExists(className, mParam.classLoader); 106 | if (javaMethodMapClass == null) { 107 | log("className:" + className + " is null"); 108 | continue; 109 | } 110 | String[] array = (String[]) XposedHelpers.callStaticMethod(javaMethodMapClass, Constant.JAVA_METHOD_ARRAY_GET_METHOD_NAME); 111 | log("map:" + Arrays.toString(array)); 112 | if (array == null || array.length == 0) { 113 | continue; 114 | } 115 | for (String cName : array) { 116 | JavaMethod value = null; 117 | try { 118 | Object obj = Class.forName(cName).newInstance(); 119 | if (obj instanceof JavaMethod) { 120 | value = (JavaMethod) obj; 121 | } else { 122 | log(cName + " 未继承[" + cName + "_],忽略"); 123 | } 124 | } catch (Exception e) { 125 | log(e); 126 | } 127 | addJavaMethod(value, false); 128 | } 129 | } 130 | } 131 | } 132 | 133 | private void initCallLog() { 134 | if (!JsPosedConfig.debug) { 135 | return; 136 | } 137 | XposedBridge.hookAllConstructors(JavaMethod.class, new XC_MethodHook() { 138 | @Override 139 | protected void beforeHookedMethod(MethodHookParam param) { 140 | Class objectClass = param.thisObject.getClass(); 141 | List list = new ArrayList<>(); 142 | Method[] methods = objectClass.getDeclaredMethods(); 143 | for (Method method : methods) { 144 | if (!list.contains(method.getName())) { 145 | list.add(method.getName()); 146 | } 147 | } 148 | methods = objectClass.getMethods(); 149 | for (Method method : methods) { 150 | if (!list.contains(method.getName())) { 151 | list.add(method.getName()); 152 | } 153 | } 154 | for (String s : list) { 155 | XposedBridge.hookAllMethods(objectClass, s, mXC_methodHook); 156 | } 157 | } 158 | }); 159 | } 160 | 161 | private XC_MethodHook mXC_methodHook = new XC_MethodHook() { 162 | @Override 163 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable { 164 | LogUtils.log(param.thisObject.getClass().getName() + "#" + param.method.getName() + " before called,args :" + Arrays.toString(param.args)); 165 | super.beforeHookedMethod(param); 166 | } 167 | 168 | @Override 169 | protected void afterHookedMethod(MethodHookParam param) throws Throwable { 170 | LogUtils.log(param.thisObject.getClass().getName() + "#" + param.method.getName() + " after called,result :" + param.getResult()); 171 | super.afterHookedMethod(param); 172 | } 173 | }; 174 | 175 | private void checkJsChanged(String jsContent) { 176 | if (TextUtils.equals(js, jsContent)) { 177 | return; 178 | } 179 | js = jsContent; 180 | run(js); 181 | } 182 | 183 | @Override 184 | public void run(String js) { 185 | try { 186 | mContext.evaluateString(scope, js, mParam.packageName, 1, null); 187 | } catch (Throwable t) { 188 | log(t); 189 | } 190 | } 191 | 192 | @Override 193 | public void call(Function call, Object... args) { 194 | try { 195 | if (call == null) { 196 | return; 197 | } 198 | call.call(mContext, scope, scope, args); 199 | } catch (Throwable t) { 200 | log(t); 201 | } 202 | } 203 | 204 | public void addJavaMethod(JavaMethod javaMethod) { 205 | addJavaMethod(javaMethod, true); 206 | } 207 | 208 | public void addJavaMethod(JavaMethod javaMethod, boolean addSuperClass) { 209 | if (javaMethod == null) { 210 | return; 211 | } 212 | if (javaMethods.contains(javaMethod.getJavaMethodName())) { 213 | return; 214 | } 215 | log("addJavaMethod:" + javaMethod); 216 | javaMethod.setJsPosedExecutor(this); 217 | javaMethod.setParam(mParam); 218 | ScriptableObject.putProperty(scope, javaMethod.getJavaMethodName(), Context.javaToJS(javaMethod, scope)); 219 | javaMethods.add(javaMethod.getJavaMethodName()); 220 | if (!addSuperClass) { 221 | return; 222 | } 223 | Class superclass = javaMethod.getClass().getSuperclass(); 224 | if (superclass != null && JavaMethod.class.isAssignableFrom(superclass) && JavaMethod.class != superclass && !javaMethods.contains(superclass)) { 225 | try { 226 | Constructor constructor = XposedHelpers.findConstructorBestMatch(superclass); 227 | if (constructor != null) { 228 | JavaMethod parentInstance = (JavaMethod) constructor.newInstance(); 229 | addJavaMethod(parentInstance); 230 | } 231 | } catch (Exception e) { 232 | e.printStackTrace(); 233 | } 234 | } 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /jsposed/src/main/java/com/wrbug/jsposed/JsPosedReceiver.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposed; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | 7 | import org.mozilla.javascript.Function; 8 | 9 | public class JsPosedReceiver extends BroadcastReceiver { 10 | private Callback mCallback; 11 | 12 | public JsPosedReceiver(Callback callback) { 13 | mCallback = callback; 14 | } 15 | 16 | @Override 17 | public void onReceive(Context context, Intent intent) { 18 | if (mCallback != null) { 19 | mCallback.onReceive(context, intent); 20 | } 21 | } 22 | 23 | 24 | public abstract static class Callback { 25 | protected Function mFunction; 26 | 27 | public Callback(Function function) { 28 | mFunction = function; 29 | } 30 | 31 | public abstract void onReceive(Context context, Intent intent); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /jsposed/src/main/java/com/wrbug/jsposed/LogUtils.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposed; 2 | 3 | import de.robv.android.xposed.XposedBridge; 4 | 5 | public class LogUtils { 6 | private static final String DEBUG_TAG = "jsPosed.debug-->"; 7 | 8 | public static void log(Object o) { 9 | if (!JsPosedConfig.debug) { 10 | return; 11 | } 12 | XposedBridge.log(DEBUG_TAG + String.valueOf(o)); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /jsposed/src/main/java/com/wrbug/jsposed/jscall/JsBundle.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposed.jscall; 2 | 3 | import android.os.Bundle; 4 | import android.os.PersistableBundle; 5 | 6 | import com.wrbug.jsposed.jclass.build.jsposed.JsBundle_; 7 | import com.wrbug.jsposedannotation.JavaClass; 8 | 9 | @JavaClass(Bundle.class) 10 | public class JsBundle extends JsBundle_ { 11 | public JsBundle(PersistableBundle arg0) { 12 | super(arg0); 13 | } 14 | 15 | public JsBundle(Bundle arg0) { 16 | super(arg0); 17 | } 18 | 19 | public JsBundle(int arg0) { 20 | super(arg0); 21 | } 22 | 23 | public JsBundle(ClassLoader arg0) { 24 | super(arg0); 25 | } 26 | 27 | public JsBundle() { 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /jsposed/src/main/java/com/wrbug/jsposed/jscall/JsIntent.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposed.jscall; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.net.Uri; 6 | 7 | import com.wrbug.jsposed.jclass.build.jsposed.JsIntent_; 8 | import com.wrbug.jsposedannotation.JavaClass; 9 | 10 | @JavaClass(value = Intent.class, name = "JsIntent") 11 | public class JsIntent extends JsIntent_ { 12 | public JsIntent(String arg0, Uri arg1, Context arg2, Class arg3) { 13 | super(arg0, arg1, arg2, arg3); 14 | } 15 | 16 | public JsIntent(Context arg0, Class arg1) { 17 | super(arg0, arg1); 18 | } 19 | 20 | public JsIntent(String arg0, Uri arg1) { 21 | super(arg0, arg1); 22 | } 23 | 24 | public JsIntent() { 25 | } 26 | 27 | public JsIntent(Intent arg0) { 28 | super(arg0); 29 | } 30 | 31 | public JsIntent(String arg0) { 32 | super(arg0); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /jsposed/src/main/java/com/wrbug/jsposed/jscall/context/JsActivity.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposed.jscall.context; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.view.View; 6 | 7 | import com.wrbug.jsposed.jclass.build.jsposed.JsActivity_; 8 | import com.wrbug.jsposedannotation.JavaClass; 9 | 10 | @JavaClass(value = Activity.class) 11 | public class JsActivity extends JsActivity_ { 12 | 13 | public View findViewById(String idName) { 14 | return findViewById(getResources().getIdentifier(idName, "id", getPackageName())); 15 | } 16 | 17 | 18 | } 19 | -------------------------------------------------------------------------------- /jsposed/src/main/java/com/wrbug/jsposed/jscall/context/JsContext.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposed.jscall.context; 2 | 3 | import android.content.Context; 4 | 5 | import com.wrbug.jsposed.jclass.build.jsposed.JsContext_; 6 | import com.wrbug.jsposedannotation.JavaClass; 7 | 8 | @JavaClass(Context.class) 9 | public class JsContext extends JsContext_ { 10 | public JsContext() { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /jsposed/src/main/java/com/wrbug/jsposed/jscall/view/JsButton.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposed.jscall.view; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.widget.Button; 6 | 7 | import com.wrbug.jsposed.jclass.build.jsposed.JsButton_; 8 | import com.wrbug.jsposedannotation.JavaClass; 9 | 10 | @JavaClass(Button.class) 11 | public class JsButton extends JsButton_ { 12 | public JsButton(Context arg0, AttributeSet arg1, int arg2, int arg3) { 13 | super(arg0, arg1, arg2, arg3); 14 | } 15 | 16 | public JsButton(Context arg0, AttributeSet arg1, int arg2) { 17 | super(arg0, arg1, arg2); 18 | } 19 | 20 | public JsButton(Context arg0, AttributeSet arg1) { 21 | super(arg0, arg1); 22 | } 23 | 24 | public JsButton(Context arg0) { 25 | super(arg0); 26 | } 27 | 28 | public JsButton() { 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /jsposed/src/main/java/com/wrbug/jsposed/jscall/view/JsCheckBox.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposed.jscall.view; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.widget.CheckBox; 6 | 7 | import com.wrbug.jsposed.jclass.build.jsposed.JsCheckBox_; 8 | import com.wrbug.jsposedannotation.JavaClass; 9 | 10 | @JavaClass(CheckBox.class) 11 | public class JsCheckBox extends JsCheckBox_ { 12 | public JsCheckBox(Context arg0, AttributeSet arg1, int arg2, int arg3) { 13 | super(arg0, arg1, arg2, arg3); 14 | } 15 | 16 | public JsCheckBox(Context arg0, AttributeSet arg1, int arg2) { 17 | super(arg0, arg1, arg2); 18 | } 19 | 20 | public JsCheckBox(Context arg0, AttributeSet arg1) { 21 | super(arg0, arg1); 22 | } 23 | 24 | public JsCheckBox(Context arg0) { 25 | super(arg0); 26 | } 27 | 28 | public JsCheckBox() { 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /jsposed/src/main/java/com/wrbug/jsposed/jscall/view/JsCompoundButton.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposed.jscall.view; 2 | 3 | import android.widget.CompoundButton; 4 | 5 | import com.wrbug.jsposed.jclass.build.jsposed.JsCompoundButton_; 6 | import com.wrbug.jsposedannotation.JavaClass; 7 | 8 | @JavaClass(CompoundButton.class) 9 | public class JsCompoundButton extends JsCompoundButton_ { 10 | public JsCompoundButton() { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /jsposed/src/main/java/com/wrbug/jsposed/jscall/view/JsTextView.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposed.jscall.view; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.widget.TextView; 6 | 7 | import com.wrbug.jsposed.jclass.build.jsposed.JsTextView_; 8 | import com.wrbug.jsposedannotation.JavaClass; 9 | 10 | @JavaClass(TextView.class) 11 | public class JsTextView extends JsTextView_ { 12 | public JsTextView(Context arg0, AttributeSet arg1, int arg2, int arg3) { 13 | super(arg0, arg1, arg2, arg3); 14 | } 15 | 16 | public JsTextView(Context arg0, AttributeSet arg1, int arg2) { 17 | super(arg0, arg1, arg2); 18 | } 19 | 20 | public JsTextView(Context arg0, AttributeSet arg1) { 21 | super(arg0, arg1); 22 | } 23 | 24 | public JsTextView(Context arg0) { 25 | super(arg0); 26 | } 27 | 28 | public JsTextView() { 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /jsposed/src/main/java/com/wrbug/jsposed/jscall/view/JsView.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposed.jscall.view; 2 | 3 | import android.content.Context; 4 | import android.graphics.Color; 5 | import android.util.AttributeSet; 6 | import android.view.View; 7 | 8 | import com.wrbug.jsposed.jclass.build.jsposed.JsView_; 9 | import com.wrbug.jsposedannotation.JavaClass; 10 | 11 | 12 | @JavaClass(View.class) 13 | public class JsView extends JsView_ { 14 | public JsView(Context arg0, AttributeSet arg1, int arg2, int arg3) { 15 | super(arg0, arg1, arg2, arg3); 16 | } 17 | 18 | public JsView(Context arg0, AttributeSet arg1, int arg2) { 19 | super(arg0, arg1, arg2); 20 | } 21 | 22 | public JsView(Context arg0, AttributeSet arg1) { 23 | super(arg0, arg1); 24 | } 25 | 26 | public JsView(Context arg0) { 27 | super(arg0); 28 | } 29 | 30 | public JsView() { 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /jsposed/src/main/java/com/wrbug/jsposed/jscall/view/JsViewGroup.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposed.jscall.view; 2 | 3 | import android.view.ViewGroup; 4 | 5 | import com.wrbug.jsposed.jclass.build.jsposed.JsViewGroup_; 6 | import com.wrbug.jsposedannotation.JavaClass; 7 | 8 | @JavaClass(ViewGroup.class) 9 | public class JsViewGroup extends JsViewGroup_ { 10 | public JsViewGroup() { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /jsposed/src/main/java/com/wrbug/jsposed/jscall/xposed/Env.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposed.jscall.xposed; 2 | 3 | import android.content.pm.ApplicationInfo; 4 | import android.os.Build; 5 | 6 | import com.wrbug.jsposedannotation.JavaMethod; 7 | 8 | import de.robv.android.xposed.XposedHelpers; 9 | 10 | public class Env extends JavaMethod { 11 | 12 | 13 | @Override 14 | public String getJavaMethodName() { 15 | return "Env"; 16 | } 17 | 18 | public String packageName() { 19 | return mParam.packageName; 20 | } 21 | 22 | public String processName() { 23 | return mParam.processName; 24 | } 25 | 26 | public ClassLoader classLoader() { 27 | return mParam.classLoader; 28 | } 29 | 30 | public ApplicationInfo appInfo() { 31 | return mParam.appInfo; 32 | } 33 | 34 | public boolean isFirstApplication() { 35 | return mParam.isFirstApplication; 36 | } 37 | 38 | public int sdkInit() { 39 | return Build.VERSION.SDK_INT; 40 | } 41 | 42 | public Object buildVersion(String fieldName) { 43 | return XposedHelpers.getStaticObjectField(Build.VERSION.class, fieldName); 44 | } 45 | 46 | public Object field(String fieldName) { 47 | return XposedHelpers.getObjectField(mParam, fieldName); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /jsposed/src/main/java/com/wrbug/jsposed/jscall/xposed/JsPosedBridge.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposed.jscall.xposed; 2 | 3 | import com.wrbug.jsposed.ClassUtils; 4 | import com.wrbug.jsposed.CommonMethodHook; 5 | import com.wrbug.jsposedannotation.JavaMethod; 6 | 7 | import org.mozilla.javascript.Function; 8 | 9 | import java.util.Set; 10 | 11 | import de.robv.android.xposed.XC_MethodHook; 12 | import de.robv.android.xposed.XposedBridge; 13 | 14 | public class JsPosedBridge extends JavaMethod { 15 | 16 | 17 | @Override 18 | public String getJavaMethodName() { 19 | return "JsPosedBridge"; 20 | } 21 | 22 | public void log(Object msg) { 23 | XposedBridge.log(String.valueOf(msg)); 24 | } 25 | 26 | public void log(Throwable throwable) { 27 | XposedBridge.log(throwable); 28 | } 29 | 30 | public void log(String tag, String msg) { 31 | XposedBridge.log(tag + "--->" + msg); 32 | } 33 | 34 | public int getXposedVersion() { 35 | return XposedBridge.getXposedVersion(); 36 | } 37 | 38 | 39 | /** 40 | * @param hookClass support Class or String , 41 | * eg. "java.lang.String","java.util.Map" 42 | * @param methodName 43 | * @param beforeCall XC_MethodHook#beforeHookedMethod callback 44 | * @param afterCall XC_MethodHook#afterHookedMethod callback 45 | * @return 46 | */ 47 | public Set hookAllMethods(Object hookClass, String methodName, Function beforeCall, Function afterCall) { 48 | Class clazz = ClassUtils.toClass(hookClass, mParam.classLoader); 49 | return XposedBridge.hookAllMethods(clazz, methodName, new CommonMethodHook(mJsPosedExecutor, beforeCall, afterCall)); 50 | } 51 | 52 | public Set hookAllConstructors(Object hookClass, Function beforeCall, Function afterCall) { 53 | Class clazz = ClassUtils.toClass(hookClass, mParam.classLoader); 54 | return XposedBridge.hookAllConstructors(clazz, new CommonMethodHook(mJsPosedExecutor, beforeCall, afterCall)); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /jsposed/src/main/java/com/wrbug/jsposed/jscall/xposed/JsPosedHelpers.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposed.jscall.xposed; 2 | 3 | import com.wrbug.jsposed.ArrayManager; 4 | import com.wrbug.jsposed.ClassUtils; 5 | import com.wrbug.jsposed.CommonMethodHook; 6 | import com.wrbug.jsposed.jclass.build.jsposed.JsPosedHelpers_; 7 | import com.wrbug.jsposedannotation.JavaClass; 8 | import com.wrbug.jsposedannotation.JavaMethod; 9 | 10 | import org.mozilla.javascript.Function; 11 | 12 | import java.lang.reflect.Constructor; 13 | import java.lang.reflect.Method; 14 | 15 | import de.robv.android.xposed.XC_MethodHook; 16 | import de.robv.android.xposed.XposedHelpers; 17 | 18 | @JavaClass(XposedHelpers.class) 19 | public class JsPosedHelpers extends JsPosedHelpers_ { 20 | public Class findClass(String className) { 21 | return findClass(className, JavaMethod.mParam.classLoader); 22 | } 23 | 24 | /** 25 | * @param clazz support Class or String , 26 | * eg. "java.lang.String","java.util.Map" 27 | * @param fieldName 28 | * @return 29 | */ 30 | public Object getStaticObjectField(Object clazz, String fieldName) { 31 | Class cls = ClassUtils.toClass(clazz); 32 | if (cls == null) { 33 | return null; 34 | } 35 | return XposedHelpers.getStaticObjectField(cls, fieldName); 36 | } 37 | 38 | /** 39 | * @param clazz support Class or String , 40 | * eg. "java.lang.String","java.util.Map" 41 | * @param args 42 | * @return 43 | */ 44 | public Object newInstance(Object clazz, Object[] args) { 45 | return newInstance(clazz, XposedHelpers.getParameterTypes(args), args); 46 | } 47 | 48 | /** 49 | * @param clazz 50 | * @param parameterTypes support Class or String , 51 | * eg. "java.lang.String","java.util.Map","int" 52 | * @param args 53 | * @return 54 | */ 55 | public Object newInstance(Object clazz, Object[] parameterTypes, Object[] args) { 56 | Class[] classes = ClassUtils.toClass(JavaMethod.mParam.classLoader, parameterTypes); 57 | try { 58 | Class aClass = ClassUtils.toClass(clazz); 59 | Constructor constructor = XposedHelpers.findConstructorBestMatch(aClass, classes); 60 | if (constructor == null) { 61 | return null; 62 | } 63 | return constructor.newInstance(ClassUtils.convertObject(classes, args)); 64 | } catch (Exception e) { 65 | } 66 | return null; 67 | } 68 | 69 | public Object callStaticMethod(Object clazz, String methodName, Object[] args) { 70 | Class cls = ClassUtils.toClass(clazz); 71 | if (cls == null) { 72 | return null; 73 | } 74 | return XposedHelpers.callStaticMethod(cls, methodName, args); 75 | } 76 | 77 | public Object callMethod(Object o, String methodName, Object[] classType, Object[] args) { 78 | Class[] classes = ClassUtils.toClass(JavaMethod.mParam.classLoader, classType); 79 | args = ClassUtils.convertObject(classes, args); 80 | return XposedHelpers.callMethod(o, methodName, classes, args); 81 | } 82 | 83 | public Object callStaticMethod(Object clazz, String methodName, Object[] classType, Object[] args) { 84 | Class cls = ClassUtils.toClass(clazz); 85 | if (cls == null) { 86 | return null; 87 | } 88 | Class[] classes = ClassUtils.toClass(JavaMethod.mParam.classLoader, classType); 89 | args = ClassUtils.convertObject(classes, args); 90 | return XposedHelpers.callStaticMethod(cls, methodName, classes, args); 91 | } 92 | 93 | public Method findMethodBestMatch(Class clazz, String methodName, Object[] parameterTypes, Object[] args) { 94 | Class[] classes = ClassUtils.toClass(JavaMethod.mParam.classLoader, parameterTypes); 95 | return XposedHelpers.findMethodBestMatch(clazz, methodName, classes, args); 96 | } 97 | 98 | 99 | /** 100 | * @param clazz 101 | * @param methodName 102 | * @param argType 103 | * @param beforeCall XC_MethodHook#beforeHookedMethod callback 104 | * @param afterCall XC_MethodHook#afterHookedMethod callback 105 | * @return 106 | */ 107 | public XC_MethodHook.Unhook findAndHookMethod(Object clazz, String methodName, Object[] argType, Function beforeCall, Function afterCall) { 108 | Class cls = ClassUtils.toClass(clazz, JavaMethod.mParam.classLoader); 109 | if (cls == null) { 110 | return null; 111 | } 112 | Object[] array = ArrayManager.getInstance().addArray(argType).add(new CommonMethodHook(JavaMethod.mJsPosedExecutor, beforeCall, afterCall)).toArray(); 113 | return XposedHelpers.findAndHookMethod(cls, methodName, array); 114 | } 115 | 116 | 117 | public XC_MethodHook.Unhook findAndHookMethod(String className, ClassLoader classLoader, String methodName, Object[] argType, Function beforeCall, Function afterCall) { 118 | Object[] array = ArrayManager.getInstance().addArray(argType).add(new CommonMethodHook(JavaMethod.mJsPosedExecutor, beforeCall, afterCall)).toArray(); 119 | return XposedHelpers.findAndHookMethod(className, classLoader, methodName, array); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /jsposed/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | jsPosed 3 | 4 | -------------------------------------------------------------------------------- /jsposed/src/test/java/com/wrbug/jsposed/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposed; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /jsposedgradleplugin/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /jsposedgradleplugin/bintray.gradle: -------------------------------------------------------------------------------- 1 | group = PROJ_GROUP 2 | version = PROJ_VERSION 3 | project.archivesBaseName = PROJ_ARTIFACTID 4 | 5 | apply plugin: 'com.jfrog.bintray' 6 | apply plugin: 'com.github.dcendents.android-maven' 7 | 8 | task sourcesJar(type: Jar) { 9 | from android.sourceSets.main.java.srcDirs 10 | classifier = 'sources' 11 | } 12 | 13 | task javadoc(type: Javadoc) { 14 | source = android.sourceSets.main.java.srcDirs 15 | classpath += configurations.compile 16 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) 17 | } 18 | 19 | task javadocJar(type: Jar, dependsOn: javadoc) { 20 | classifier = 'javadoc' 21 | from javadoc.destinationDir 22 | } 23 | 24 | task syncJavadoc(type: Sync, dependsOn: javadoc) { 25 | from javadoc.destinationDir 26 | into rootProject.file('docs') 27 | } 28 | 29 | javadoc { 30 | options{ 31 | encoding "UTF-8" 32 | charSet 'UTF-8' 33 | author true 34 | version true 35 | title "$PROJ_NAME $PROJ_VERSION" 36 | String packageListRef = "${android.sdkDirectory}/docs/reference/" 37 | linksOffline 'http://d.android.com/reference/', packageListRef 38 | } 39 | } 40 | 41 | artifacts { 42 | archives sourcesJar 43 | archives javadocJar 44 | } 45 | 46 | install { 47 | repositories.mavenInstaller { 48 | pom.project { 49 | name PROJ_NAME 50 | description PROJ_DESCRIPTION 51 | url PROJ_WEBSITEURL 52 | inceptionYear '2016' 53 | 54 | packaging 'aar' 55 | groupId PROJ_GROUP 56 | artifactId PROJ_ARTIFACTID 57 | version PROJ_VERSION 58 | 59 | licenses { 60 | license { 61 | name 'The Apache Software License, Version 2.0' 62 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt' 63 | distribution 'repo' 64 | } 65 | } 66 | scm { 67 | connection PROJ_VCSURL 68 | url PROJ_WEBSITEURL 69 | 70 | } 71 | developers { 72 | developer { 73 | id DEVELOPER_ID 74 | name DEVELOPER_NAME 75 | email DEVELOPER_EMAIL 76 | } 77 | } 78 | } 79 | } 80 | } 81 | 82 | bintray { 83 | user = project.hasProperty('bintrayUser') ? project.property('bintrayUser') : getLocalProperties('BINTRAY_USER') 84 | key = project.hasProperty('bintrayKey') ? project.property('bintrayKey') : getLocalProperties('BINTRAY_KEY') 85 | 86 | configurations = ['archives'] 87 | 88 | dryRun = false 89 | publish = true 90 | 91 | pkg { 92 | repo = 'maven' 93 | name = PROJ_NAME 94 | licenses = ['Apache-2.0'] 95 | vcsUrl = PROJ_VCSURL 96 | websiteUrl = PROJ_WEBSITEURL 97 | issueTrackerUrl = PROJ_ISSUETRACKERURL 98 | publicDownloadNumbers = true 99 | version { 100 | name = PROJ_VERSION 101 | desc = PROJ_DESCRIPTION 102 | vcsTag = PROJ_VERSION 103 | 104 | gpg { 105 | sign = true 106 | } 107 | } 108 | } 109 | } 110 | 111 | 112 | def getLocalProperties(key){ 113 | File file = rootProject.file('local.properties') 114 | if(file.exists()){ 115 | InputStream inputStream = rootProject.file('local.properties').newDataInputStream(); 116 | Properties properties = new Properties() 117 | properties.load(inputStream) 118 | 119 | if(properties.containsKey(key)){ 120 | return properties.getProperty(key) 121 | } 122 | } 123 | return "" 124 | } -------------------------------------------------------------------------------- /jsposedgradleplugin/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'groovy' 2 | dependencies { 3 | implementation gradleApi() 4 | implementation localGroovy() 5 | implementation 'com.android.tools.build:gradle:3.2.1' 6 | } 7 | 8 | apply plugin: 'maven' 9 | group = 'com.wrbug.jsposedgradleplugin' 10 | version = '1.0.0' 11 | 12 | uploadArchives { 13 | repositories { 14 | mavenDeployer { 15 | //本地的Maven地址设置为D:/repos 16 | repository(url: uri('../repo')) 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /jsposedgradleplugin/gradle.properties: -------------------------------------------------------------------------------- 1 | PROJ_GROUP=com.wrbug 2 | PROJ_VERSION_CODE=2 3 | PROJ_VERSION=0.0.2 4 | PROJ_NAME=JsPosed 5 | PROJ_WEBSITEURL=https://github.com/WrBug/JsPosed 6 | PROJ_ISSUETRACKERURL=https://github.com/WrBug/JsPosed 7 | PROJ_VCSURL=git@github.com:WrBug/JsPosed.git 8 | PROJ_DESCRIPTION= 9 | PROJ_ARTIFACTID=jsposed 10 | 11 | DEVELOPER_ID= 12 | DEVELOPER_NAME= wrbug 13 | DEVELOPER_EMAIL= iwrbug@gmail.com -------------------------------------------------------------------------------- /jsposedgradleplugin/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /jsposedgradleplugin/src/androidTest/java/com/wrbug/jsposedgradleplugin/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposedgradleplugin; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.wrbug.jsposedgradleplugin.test", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /jsposedgradleplugin/src/main/groovy/com/wrbug/jsposedgradleplugin/JsPosedPlugin.groovy: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposedgradleplugin 2 | 3 | import com.android.build.gradle.AppExtension 4 | import com.android.build.gradle.AppPlugin 5 | import com.android.build.gradle.internal.api.ApplicationVariantImpl 6 | import com.android.build.gradle.internal.dsl.BaseAppModuleExtension 7 | import org.gradle.api.Plugin 8 | import org.gradle.api.Project 9 | 10 | class JsPosedPlugin implements Plugin { 11 | 12 | @Override 13 | void apply(Project project) { 14 | project. 15 | println "this is JsPosedPlugin!!" 16 | StringBuilder builder = new StringBuilder() 17 | project.afterEvaluate { 18 | Project appProject 19 | Set libs = project.rootProject.subprojects 20 | libs.each { p -> 21 | if (p.plugins.hasPlugin(AppPlugin)) { 22 | appProject = p 23 | } 24 | builder.append(p.name.replace("-", "_").toLowerCase()).append(",") 25 | 26 | } 27 | if (appProject == null) { 28 | return 29 | } 30 | builder.deleteCharAt(builder.length() - 1) 31 | println(appProject.getBuildDir()) 32 | println builder 33 | ((BaseAppModuleExtension) appProject.android).applicationVariants.all { ApplicationVariantImpl variant -> 34 | variant.buildConfigField("String", "MODULES_NAME", "\"$builder\"") 35 | } 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /jsposedgradleplugin/src/main/groovy/com/wrbug/jsposedgradleplugin/MergeJavaMethodTransform.groovy: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposedgradleplugin 2 | 3 | import com.android.build.api.transform.QualifiedContent 4 | import com.android.build.api.transform.Transform 5 | import com.android.build.api.transform.TransformException 6 | import com.android.build.api.transform.TransformInvocation 7 | import com.android.build.gradle.internal.pipeline.TransformManager 8 | import org.gradle.api.Project 9 | 10 | class MergeJavaMethodTransform extends Transform { 11 | Project mProject 12 | 13 | MergeJavaMethodTransform(Project project) { 14 | println("MergeJavaMethodTransform") 15 | mProject = project 16 | } 17 | 18 | @Override 19 | String getName() { 20 | return "MergeJavaMethod" 21 | } 22 | 23 | @Override 24 | Set getInputTypes() { 25 | return TransformManager.CONTENT_CLASS 26 | } 27 | 28 | @Override 29 | Set getOutputTypes() { 30 | return TransformManager.CONTENT_CLASS 31 | } 32 | 33 | @Override 34 | Set getScopes() { 35 | return TransformManager.SCOPE_FULL_PROJECT 36 | } 37 | 38 | @Override 39 | boolean isIncremental() { 40 | return false 41 | } 42 | 43 | @Override 44 | void transform(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException { 45 | super.transform(transformInvocation) 46 | println("transformInvocation") 47 | } 48 | } -------------------------------------------------------------------------------- /jsposedgradleplugin/src/main/resources/META-INF/gradle-plugins/com.wrbug.jsposedgradleplugin.properties: -------------------------------------------------------------------------------- 1 | implementation-class=com.wrbug.jsposedgradleplugin.JsPosedPlugin -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # JsPosed 2 | 3 | > JsPosed 是一款通过js代码调用xposed相关功能的框架,支持热部署,热更新,无需重启即可生效 4 | 5 | 6 | ### 下载体验 7 | 8 | 1. 编译项目或 [下载apk](https://github.com/WrBug/JsPosed/releases/tag/demo) ,安装到有xposed环境的设备上 9 | 10 | 2. 下载[tmp.js](https://github.com/WrBug/JsPosed/blob/master/tmp.js) 导入到手机 `/data/local/tmp` 目录下 11 | 3. 第一次需要激活重启,重启后再次打开 app即可生效 12 | 4. 可以通过修改 `tmp.js` 内容,保存后杀掉app进程再启动即可生效 13 | 14 | ![](https://i.loli.net/2019/01/10/5c37115ead403.png)![](https://i.loli.net/2019/01/10/5c37115f50b03.png) 15 | 16 | ### wiki 17 | 18 | 完整使用说明请查看[wiki](https://github.com/WrBug/JsPosed/wiki) 19 | 20 | ## 开始使用 21 | 22 | ### 配置依赖 23 | 24 | ``` 25 | 26 | implementation 'com.wrbug:jsposed:0.0.2' 27 | 28 | ``` 29 | 30 | #### 初始化 31 | 32 | 33 | 34 | ``` java 35 | @Override 36 | public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) { 37 | String js=... 38 | boolean debug=... 39 | //初始化JsPosed 40 | JsPosedExecutor executor = JsPosedExecutor.init(lpparam, jsContent, debug); 41 | //执行js方法 42 | executor.run(fun); 43 | } 44 | ``` 45 | ###### 参数说明 46 | 47 | | 参数 | 说明 | 48 | | --- | --- | 49 | | lpparam | handleLoadPackage方法参数 | 50 | | jsContent | js文本 | 51 | | debug | 打印debug日志 | 52 | | fun | 执行方法,一般为入口方法 | 53 | 54 | ###### 注意 55 | 56 | js文本可以使用硬编码或通过文件的形式获取,使用文件时需注意hook的应用是否有权限读取文件. 57 | 可以参考[XposedInit.java](https://github.com/WrBug/JsPosed/blob/master/sample/src/main/java/com/wrbug/jsposeddemo/XposedInit.java#L18) 58 | 59 | #### 简单js实现 60 | 61 | ``` javaScript 62 | function start(){ 63 | var packageName = Env.packageName(); 64 | if (packageName != "com.wrbug.jsposed") { 65 | return 66 | } 67 | 68 | //hook MainActivity#onCreate 方法 69 | JsPosedHelpers.findAndHookMethod("com.wrbug.jsposeddemo.MainActivity", "onCreate", ["android.os.Bundle"], null, function (methodHookParam) { 70 | //获取thisObject变量 71 | var activity = JsPosedHelpers.getObjectField(methodHookParam, "thisObject"); 72 | // 获取tv变量 73 | var tv = JsPosedHelpers.getObjectField(activity, "tv"); 74 | // 获取mCheckBox变量 75 | var checkbox = JsPosedHelpers.getObjectField(activity, "mCheckBox"); 76 | //通过 idName获取btn 77 | var btn = JsContext.findViewById(activity, "btn") 78 | // 设置tv文本 79 | JsView.setText(tv, "Jsposed running"); 80 | JsView.setText(btn, "点击跳转"); 81 | // 设置tv文本颜色 82 | JsView.setTextColor(tv, 0xffff0000); 83 | // 设置tv字体大小 84 | JsView.setTextSize(tv, 20); 85 | // 设置tv 点击事件 86 | JsView.setOnclickListener(tv, function (view) { 87 | JsView.toggle(checkbox) 88 | }); 89 | JsView.setOnclickListener(btn, function (view) { 90 | // 跳转到 Main2Activity 91 | JsContext.startActivity(activity, "com.wrbug.jsposeddemo.Main2Activity", { 92 | "a": "test1", 93 | "b": ["test2","java.lang.String"], 94 | "c": 12345, 95 | "d":[12345,"long"] 96 | }) 97 | }); 98 | // checkbox 事件监听 99 | JsView.setOnCheckedChangeListener(checkbox, function (view, isChecked) { 100 | JsView.setText(tv, "checkBox status:" + isChecked); 101 | }) 102 | }) 103 | } 104 | 105 | ``` 106 | 107 | 我们在框架内置了许多Js方法,大多数方法与原生方法命名相同,由于js无法操作原生对象,所以Js方法第一个参数一般穿对象实例,完整方法请查看方法对照表 108 | 109 | 110 | -------------------------------------------------------------------------------- /sample/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /sample/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'com.wrbug.jsposedgradleplugin' 3 | android { 4 | //编译需注释 5 | signingConfigs { 6 | releaseConfig 7 | } 8 | lintOptions { 9 | abortOnError false 10 | } 11 | compileSdkVersion 28 12 | defaultConfig { 13 | applicationId "com.wrbug.jsposed" 14 | minSdkVersion 21 15 | targetSdkVersion 28 16 | versionCode 1 17 | versionName "1.0" 18 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 19 | javaCompileOptions { 20 | annotationProcessorOptions { 21 | includeCompileClasspath true 22 | arguments = [moduleName: project.getName()] 23 | } 24 | } 25 | } 26 | buildTypes { 27 | debug { 28 | minifyEnabled false 29 | if (signingConfigs.releaseConfig != null) { 30 | signingConfig signingConfigs.releaseConfig 31 | } 32 | } 33 | release { 34 | minifyEnabled false 35 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 36 | if (signingConfigs.releaseConfig != null) { 37 | signingConfig signingConfigs.releaseConfig 38 | } 39 | 40 | } 41 | } 42 | } 43 | 44 | dependencies { 45 | implementation fileTree(include: ['*.jar'], dir: 'libs') 46 | implementation 'com.android.support:appcompat-v7:28.0.0' 47 | implementation 'com.android.support.constraint:constraint-layout:1.1.3' 48 | testImplementation 'junit:junit:4.12' 49 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 50 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 51 | compileOnly 'de.robv.android.xposed:api:82' 52 | implementation 'org.mozilla:rhino:1.7.10' 53 | // implementation 'com.wrbug:jsposed:0.0.1' 54 | implementation project(':jsposed') 55 | implementation project(':JsPosedAnnotation') 56 | annotationProcessor project(':JsPosedCompile') 57 | } 58 | 59 | 60 | 61 | 62 | Properties props = new Properties() 63 | def propFile = file('../signing.properties') 64 | if (propFile.canRead()) { 65 | props.load(new FileInputStream(propFile)) 66 | if (props != null && 67 | props.containsKey('SIGN_FILE') && props.containsKey('SIGN_PASSWORD') && 68 | props.containsKey('KEY_ALIAS') && props.containsKey('KEY_PASSWORD')) { 69 | android.signingConfigs.releaseConfig.storeFile = file(props['SIGN_FILE']) 70 | android.signingConfigs.releaseConfig.storePassword = props['SIGN_PASSWORD'] 71 | android.signingConfigs.releaseConfig.keyAlias = props['KEY_ALIAS'] 72 | android.signingConfigs.releaseConfig.keyPassword = props['KEY_PASSWORD'] 73 | } else { 74 | println 'release build not found signing properties' 75 | android.buildTypes.release.signingConfig = null 76 | } 77 | } else { 78 | println 'release build not found signing file' 79 | android.buildTypes.release.signingConfig = null 80 | } 81 | 82 | 83 | -------------------------------------------------------------------------------- /sample/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /sample/src/androidTest/java/com/wrbug/jsposeddemo/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposeddemo; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.wrbug.jsposed", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /sample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 23 | 26 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /sample/src/main/assets/xposed_init: -------------------------------------------------------------------------------- 1 | com.wrbug.jsposeddemo.XposedInit -------------------------------------------------------------------------------- /sample/src/main/java/com/wrbug/jsposeddemo/Main2Activity.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposeddemo; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | 6 | import com.wrbug.jsposed.R; 7 | 8 | public class Main2Activity extends AppCompatActivity { 9 | @Override 10 | protected void onCreate(Bundle savedInstanceState) { 11 | super.onCreate(savedInstanceState); 12 | setContentView(R.layout.activity_main2); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /sample/src/main/java/com/wrbug/jsposeddemo/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposeddemo; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.view.KeyEvent; 6 | import android.widget.CheckBox; 7 | import android.widget.TextView; 8 | 9 | import com.wrbug.jsposed.BuildConfig; 10 | import com.wrbug.jsposed.R; 11 | 12 | public class MainActivity extends AppCompatActivity { 13 | 14 | private TextView tv; 15 | private CheckBox mCheckBox; 16 | 17 | @Override 18 | protected void onCreate(Bundle savedInstanceState) { 19 | super.onCreate(savedInstanceState); 20 | setContentView(R.layout.activity_main); 21 | tv = findViewById(R.id.tv); 22 | tv.setText(BuildConfig.MODULES_NAME); 23 | mCheckBox = findViewById(R.id.checkbox); 24 | 25 | } 26 | 27 | @Override 28 | public boolean dispatchKeyEvent(KeyEvent event) { 29 | return super.dispatchKeyEvent(event); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /sample/src/main/java/com/wrbug/jsposeddemo/Test.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposeddemo; 2 | 3 | import com.wrbug.jsposedannotation.JavaMethod; 4 | 5 | public class Test extends JavaMethod { 6 | 7 | @Override 8 | public String getJavaMethodName() { 9 | return "Test"; 10 | } 11 | 12 | public void test(String str) { 13 | System.out.println(str); 14 | } 15 | 16 | 17 | public int test(int num) { 18 | return num * num; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /sample/src/main/java/com/wrbug/jsposeddemo/XposedInit.java: -------------------------------------------------------------------------------- 1 | package com.wrbug.jsposeddemo; 2 | 3 | import android.text.TextUtils; 4 | 5 | import com.wrbug.jsposed.JsPosedExecutor; 6 | 7 | import java.io.File; 8 | import java.io.FileReader; 9 | import java.io.IOException; 10 | 11 | import de.robv.android.xposed.IXposedHookLoadPackage; 12 | import de.robv.android.xposed.callbacks.XC_LoadPackage; 13 | 14 | public class XposedInit implements IXposedHookLoadPackage { 15 | 16 | @Override 17 | public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) { 18 | String str = readFile(new File("/data/local/tmp/tmp.js")); 19 | if (TextUtils.isEmpty(str)) { 20 | return; 21 | } 22 | JsPosedExecutor executor = JsPosedExecutor.init(lpparam, str, lpparam.packageName.equals("com.wrbug.jsposed")); 23 | executor.addJavaMethod(new Test()); 24 | executor.run("start()"); 25 | } 26 | 27 | public static String readFile(File file) { 28 | StringBuilder builder = new StringBuilder(); 29 | try { 30 | FileReader fr = new FileReader(file); 31 | int ch = 0; 32 | while ((ch = fr.read()) != -1) { 33 | builder.append((char) ch); 34 | } 35 | } catch (IOException e) { 36 | } 37 | return builder.toString(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /sample/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /sample/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 20 | 21 | 26 | 27 |