2

I am trying to get the app code and display it, for an example if button X starts a new activity then a textView displays the whole method

I reached only how can I display code in HTML format from this question

But is there is a way to get the code of my app out, I think that there are 2 ways

  1. An Internal one by getting it by the app itself
  2. An External one by reading the java file then filtering it and getting the text of the method

Is there are any ideas about that?

Thanks in advance

Community
  • 1
  • 1
Dasser Basyouni
  • 3,142
  • 5
  • 26
  • 50
  • 2
    I believe this is no easy task as that information is no longer available at runtime and you would need to decompile the code from bytecode. Refer to this answer http://stackoverflow.com/questions/2946279/how-do-i-print-the-method-body-reflectively for more info. – Edson Menegatti Dec 29 '16 at 16:33
  • Maybe still there is a way, that's good. to search and know about hoping the better way, thank you – Dasser Basyouni Dec 29 '16 at 16:45
  • 1
    "then a textView displays the whole method" -- **what** "whole method"? – CommonsWare Dec 29 '16 at 17:06
  • any method for example a method thats starts an activity with intent then the textview display the intent code andy the method name – Dasser Basyouni Dec 30 '16 at 00:44
  • I'd say, due to the nature of Java (and therefore android) this is not currently possible. Because Java is a compiled then ran language (vs python which is an interpreted and live language), any sort of "source" code that is not expressly provided by the developer would be stuck in a sort of bytecode. Decoding this bytecode would get you an obfuscated mess. – miversen33 Jan 03 '17 at 00:51
  • @T-he-game maybe there is a way a one or me will answer it some days, or even Google add a method for that – Dasser Basyouni Jan 03 '17 at 16:28
  • @DasserBasyouni This is possible. Use DexClassLoader + Reflection, see my answer below. – Faraz Jan 04 '17 at 17:55

2 Answers2

3

The above is not currently possible as mentioned by others is the comments. What i can suggest is shipping your application with the source code in the assets folder and using a helper function to extract a certain methods from the source at runtime (your second proposed approach). I have written example code but it is in pure java and needs to be ported to android (a few lines).

NB: You may need to reformat the code after extraction depending on your use case.

Hope it helps :)

The code for the helper method:

static String getTheCode(String classname ,String methodSignature ) throws FileNotFoundException {

     //**********************A few lines of code below need changing when porting ***********//

    // open file, your will be in the assets folder not in the home dir of user, don't forget the .java extension when porting

    File file = new File(System.getProperty("user.home") +"/"+ classname +".java");

    // get the source, you can use FileInputReader or some reader supported by android

    Scanner scanner = new Scanner(file);

    String source = "";
    while(scanner.hasNext()) {
       source += " "+ scanner.next();
    }


    //**********************The above code needs changing when porting **********//

    // extract code using the method signature

    methodSignature = methodSignature.trim();
    source = source.trim();

    //appending { to differentiate from argument as it can be matched also if in the same file
    methodSignature = methodSignature+"{";

    //making sure we find what we are looking for
    methodSignature = methodSignature.replaceAll("\\s*[(]\\s*", "(");
    methodSignature = methodSignature.replaceAll("\\s*[)]\\s*", ")");
    methodSignature = methodSignature.replaceAll("\\s*[,]\\s*", ",");
    methodSignature = methodSignature.replaceAll("\\s+", " ");


    source =source.replaceAll("\\s*[(]\\s*", "(");
    source = source.replaceAll("\\s*[)]\\s*", ")");
    source = source.replaceAll("\\s*[,]\\s*", ",");
    source = source.replaceAll("\\s+", " ");


    if(!source.contains(methodSignature)) return null;

    // trimming all text b4 method signature
    source = source.substring(source.indexOf(methodSignature));

    //getting last index, a methods ends when there are matching pairs of these {}
    int lastIndex = 0;

    int rightBraceCount = 0;
    int leftBraceCount = 0;

    char [] remainingSource = source.toCharArray();
    for (int i = 0; i < remainingSource.length ; i++
         ) {

        if(remainingSource[i] == '}'){

            rightBraceCount++;

            if(rightBraceCount == leftBraceCount){

                lastIndex = (i + 1);
                break;
            }

        }else if(remainingSource[i] == '{'){

            leftBraceCount++;
        }




    }


    return  source.substring(0 ,lastIndex);

}

Example usage (getTheCode methods is static and in a class called GetTheCode):

public static void main(String... s) throws FileNotFoundException {



    System.out.println(GetTheCode.getTheCode("Main", "private static void shoutOut()"));
    System.out.println(GetTheCode.getTheCode("Main", "private static void shoutOut(String word)"));


}

Output:

private static void shoutOut(){ // nothing to here }
private static void shoutOut(String word){ // nothing to here }

NB: When starting your new activity create a method eg

 private void myStartActivty(){

 Intent intent = new Intent(MyActivity.this, AnotherActivity.class);

    startActivity(intent);

}

Then in your onClick:

@Override
public void onClick(View v) {
    myStartActivity();

    myTextView.setText(GetTheCode.getTheCode("MyActivity","private void myStartActivity()"));
}

Update: Ported the Code for android:

    import android.content.Context;

    import java.io.IOException;
    import java.util.Scanner;

    public class GetTheCode {


static String getTheCode(Context context, String classname , String methodSignature ) {

   Scanner scanner = null;
    String source = "";
    try {
        scanner = new Scanner(context.getAssets().open(classname+".java"));



    while(scanner.hasNext()) {
       source += " "+ scanner.next();
    }

    } catch (IOException e) {
        e.printStackTrace();
        return null;
    }
        scanner.close();

    // extract code using the method signature

    methodSignature = methodSignature.trim();
    source = source.trim();

    //appending { to differentiate from argument as it can be matched also if in the same file
    methodSignature = methodSignature+"{";

    //making sure we find what we are looking for
    methodSignature = methodSignature.replaceAll("\\s*[(]\\s*", "(");
    methodSignature = methodSignature.replaceAll("\\s*[)]\\s*", ")");
    methodSignature = methodSignature.replaceAll("\\s*[,]\\s*", ",");
    methodSignature = methodSignature.replaceAll("\\s+", " ");


    source =source.replaceAll("\\s*[(]\\s*", "(");
    source = source.replaceAll("\\s*[)]\\s*", ")");
    source = source.replaceAll("\\s*[,]\\s*", ",");
    source = source.replaceAll("\\s+", " ");


    if(!source.contains(methodSignature)) return null;

    // trimming all text b4 method signature
    source = source.substring(source.indexOf(methodSignature));

    //getting last index, a methods ends when there are matching pairs of these {}
    int lastIndex = 0;

    int rightBraceCount = 0;
    int leftBraceCount = 0;

    char [] remainingSource = source.toCharArray();
    for (int i = 0; i < remainingSource.length ; i++
         ) {

        if(remainingSource[i] == '}'){

            rightBraceCount++;

            if(rightBraceCount == leftBraceCount){

                lastIndex = (i + 1);
                break;
            }

        }else if(remainingSource[i] == '{'){

            leftBraceCount++;
        }




    }


    return  source.substring(0,lastIndex);

   }

}

Usage:

   // the method now takes in context as the first parameter, the line below was in an Activity
  Log.d("tag",GetTheCode.getTheCode(this,"MapsActivity","protected void onCreate(Bundle savedInstanceState)"));
tapsey
  • 456
  • 3
  • 14
  • thank you for sure, but I have tried it after porting it is not working, I guess I did something wrong with porting, will you start a discussion with me to figure it out with me? – Dasser Basyouni Jan 03 '17 at 15:28
  • I have ported the code for android. This is where you place the assets folder **app/src/main/assets/** – tapsey Jan 04 '17 at 19:22
  • I have ported the code for android. This is where you place the assets folder **app/src/main/assets/** .Place the source files there, sorry i don't have readily available internet connection so the discussion path is tricky. Start by showing the code in a toast then a textview later on – tapsey Jan 04 '17 at 19:30
  • It is possible to run the code dynamically, this is just showing how to extract code from an asset file which is bundled with the application at build time. – Faraz Jan 04 '17 at 20:48
  • @tapsey I have tested it, but it also not working giving NullPointerException at the Log line when it is in OnClickListner() of in OnCreate() – Dasser Basyouni Jan 05 '17 at 04:11
  • See how i did it [here](https://github.com/tapsey/SecurePlayer). when you place the source in the assets folder they should be not in any folder. It should be eg app/src/main/assets/Example.java Also if it works when programming in the future the getTheCode() method may return null so it is important that you first do a null check its call. – tapsey Jan 05 '17 at 05:19
  • that works, but have we need to create 2 copies of class, one for normal use in java folder and the other in assets folder? – Dasser Basyouni Jan 05 '17 at 21:31
  • Yes, you ship the app with a copy of your code sources... its a work around since in java there is no direct way of getting the source from the byte code... – tapsey Jan 06 '17 at 18:46
2

Let's start with a broader overview of the problem:

  1. Display App code

  2. Press X button

  3. Open new activity with a textview which displays the method

The goal is to do the following:

  1. Viewing app method by extracting it and then building & running it.

There are some methods we can use to run Java/Android code dynamically. The way I would personally do it is DexClassLoader and with Reflection.

If you need more details, let me know. Here is what it'd do though:

  1. View app method
  2. Upon pressing X, launch intent with extra to new Activity
  3. Parse and compile code dynamically and then run it with DexClassLoader and Reflection

Sources:

Sample file loading Java method from TerminalIDE Android App

Android Library I made for Auto-Updating Android Applications without needing the Play Store on non-root devices

Faraz
  • 643
  • 4
  • 15
  • 1
    will you explain more for me you library and the method you are using – Dasser Basyouni Jan 04 '17 at 18:13
  • Sure. So, personally I am grabbing an APK and decompiling it in my library, I use reflection to take out the particular Fragments which are in the external APK. See this: https://github.com/farazfazli/silo/blob/master/blaise/src/main/java/com/farazfazli/blaise/BlaiseService.java#L118 what you would do is use this on a .java file https://github.com/patilswapnilv/TerminalIDE/blob/037ca08941f5ff26b521d5dc1528edb76e13a0d5/TermIDE/src/com/sun/tools/javac/Main.java#L87 and then use DexClassLoader to reflect out the class. – Faraz Jan 04 '17 at 20:45
  • I'm sorry for that but your library is very complex for me, will explain its basic methods steps briefly that get the code, also Also your compilation method (to test it for me, I have copied the whole needed classes) – Dasser Basyouni Jan 05 '17 at 21:30
  • @DasserBasyouni I would suggest you read some of the source of DexClassLoader: https://android.googlesource.com/platform/libcore/+/09bb615/dalvik/src/main/java/dalvik/system/DexClassLoader.java#26 . Here is as simple as I can explain it: you have to implement javac as shown in the TerminalIDE repo. Once you have compiled the java file into a class, you can use a version of dx implemented in Android to add classes.dex, and then use DexClassLoader to extract it. https://github.com/patilswapnilv/TerminalIDE/blob/037ca08941f5ff26b521d5dc1528edb76e13a0d5/TermIDE/src/com/android/dx/command/Main.java – Faraz Jan 06 '17 at 18:36
  • 1
    @DasserBasyouni Know that what you're trying to do is not easy. There isn't much documentation on how to use it available, because not many people dynamically load (most don't even know you can dynamically load) code in their Android apps as you are trying to do. – Faraz Jan 06 '17 at 18:39