content: package mobi.acpm.inspeckage.util;
  
  import android.content.SharedPreferences;
  import android.util.Log;
  
  import com.google.gson.Gson;
  import com.google.gson.GsonBuilder;
  import com.google.gson.JsonElement;
  import com.google.gson.JsonObject;
  
  import java.lang.reflect.Method;
  import java.lang.reflect.Modifier;
  import java.util.ArrayList;
  import java.util.Enumeration;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  import dalvik.system.DexFile;
  import de.robv.android.xposed.callbacks.XC_LoadPackage;
  
  import static android.text.TextUtils.isDigitsOnly;
  
  /**
   * Created by acpm on 19/09/16.
   */
  public class DexUtil {
  
  
      public static Map<String, ArrayList<String>> getClassesWithMethods(XC_LoadPackage.LoadPackageParam loadPackageParam, String packageName) throws Throwable {
  
          Map<String, ArrayList<String>> classes = new HashMap<String, ArrayList<String>>();
  
          if (!packageName.trim().equals("") && loadPackageParam.appInfo.sourceDir.contains(packageName)) {
  
              DexFile dexFile = new DexFile(loadPackageParam.appInfo.sourceDir);
  
              Enumeration<String> classNames = dexFile.entries();
  
              while (classNames.hasMoreElements()) {
                  final String className = classNames.nextElement();
  
                  //if (!packageName.trim().equals("") && className.contains(packageName)) {
  
                  boolean subMethod = false;
                  if (className.contains("$")) {
                      String v = className.split("\\$")[1];
                      if (isDigitsOnly(v)) {
                          subMethod = true;
                      }
                  }
  
                  if (!subMethod && !className.contains(".R$")) {
  
                      try {
                          final Class cls = Class.forName(className, false, loadPackageParam.classLoader);
  
                          if (cls != null && cls.getDeclaredMethods().length > 0) {
                              ArrayList<String> methods = new ArrayList<>();
                              for (final Method method : cls.getDeclaredMethods()) {
                                  if (!Modifier.isAbstract(method.getModifiers()) && !methods.contains(method.getName())) {
                                      methods.add(method.getName());
                                  }
                              }
                              classes.put(className, methods);
                          }
                      } catch (NoClassDefFoundError ex) {
                          Log.e("Error", ex.getMessage());
                      } catch (ClassNotFoundException ex) {
                          Log.e("Error", ex.getMessage());
                      }
  
                  }
                  //}
              }
          }
          return classes;
      }
  
      public static void saveClassesWithMethodsJson(XC_LoadPackage.LoadPackageParam loadPackageParam, SharedPreferences prefs) throws Throwable {
  
          String packageName = prefs.getString("package", "");
          Map<String, ArrayList<String>> classes = DexUtil.getClassesWithMethods(loadPackageParam, packageName);
  
          //RAIZ
          ClassMethod root = new ClassMethod();
          root.setID("p_" + packageName);
          root.setName(packageName);
  
          int c_id = 0;
          for (String classNameComplete : classes.keySet()) {
  
              if (classNameComplete.contains(packageName)) {
                  c_id++;
                  String pack_name = classNameComplete.substring(0, classNameComplete.lastIndexOf("."));
                  String class_name = classNameComplete.substring(classNameComplete.lastIndexOf(".") + 1);
  
                  //pacote
                  ClassMethod package_class = new ClassMethod();
                  package_class.setID(pack_name);
                  package_class.setName(pack_name);
  
                  if (!root.contains(package_class)) {
                      root.getClassMethods().add(package_class);
                  }
  
                  //classe
                  ClassMethod class_leaf = new ClassMethod();
                  class_leaf.setID(classNameComplete);
                  class_leaf.setName(class_name);
  
                  //adiciona metodos nas folhas(ultimas classes)
                  ArrayList<String> methods = classes.get(classNameComplete);
                  int m_id = 0;
                  for (String method : methods) {
                      m_id++;
                      ClassMethod m = new ClassMethod();
                      m.setID("m_" + c_id+"_"+m_id);//
                      m.setName(method);
                      m.setIcon("jstree-file");
                      if (!class_leaf.contains(m)) {
                          class_leaf.getClassMethods().add(m);
                      }
                  }
  
                  package_class.getClassMethods().add(class_leaf);
                  root.update(package_class);
  
              } else {
  
                  /**String name = classNameComplete.substring(0,classNameComplete.lastIndexOf("."));
  
                   ClassMethod cx = new ClassMethod();
                   cx.setID(name);
                   cx.setName(name);
  
                   if(!array.contains(cx)){
                   //c.getClassMethods().add(cx);
                   }**/
              }
          }
  
          Gson gson = new GsonBuilder().create();
          JsonElement jsonElement = gson.toJsonTree(root);
          JsonObject jsonObject = jsonElement.getAsJsonObject();
  
          FileUtil.writeToFile(prefs, jsonObject.toString(), FileType.APP_STRUCT, "");
      }
  
  
      public static class ClassMethod {
          private String id;
          private String text;
          private String icon;
  
          private List<ClassMethod> children = new ArrayList<ClassMethod>();
  
          public String getID() {
              return id;
          }
  
          public void setID(String id) {
              this.id = id;
          }
  
          public String getName() {
              return text;
          }
  
          public void setName(String name) {
              this.text = name;
          }
  
          public String getIcon() {
              return icon;
          }
  
          public void setIcon(String icon) {
              this.icon = icon;
          }
  
  
          public List<ClassMethod> getClassMethods() {
              return children;
          }
  
          public void setClassMethods(List<ClassMethod> children) {
              this.children = children;
          }
  
          public boolean contains(ClassMethod cm) {
              boolean x = false;
  
              for (ClassMethod c : getClassMethods()) {
                  if (c.getID().equals(cm.getID())) {
                      x = true;
                  }
              }
  
              return x;
          }
  
          //se ja existir a classe entao pega os metodos da nova e atualiza a que ja existe
          public boolean update(ClassMethod cm) {
              boolean x = false;
  
              for (ClassMethod c : getClassMethods()) {
                  if (c.getID().equals(cm.getID())) {
                      for (ClassMethod cm2 : cm.getClassMethods()) {
                          if (!c.contains(cm2)) {
                              c.getClassMethods().add(cm2);
                          }
                      }
                      x = true;
                  }
              }
  
  
              return x;
          }
      }
  }
  