Using AndroidJavaClass to return installed apps

I’m trying to make a launcher using Unity, so far its going much better than expected, I’m at my last hurdle which is dealing with the APP Drawer, I need help with this part because I can’t find anything helpful anywhere.
I’m desperate to figure it out at this point, me posting to forums for help is like ghost rarity TCG exclusive shooting quasar dragon rare (yu-gi-oh), if that gives you any clue haha.
I’ve already read through everything related I could find here, in the documentation, in android’s documentation, stackoverflow, even random code blogs I found on google.

To the point: I need a way to return the URI, name, and icon (either link to it, or the Texture2D itself) for every app installed (android platform specifically).
What I was thinking was the code at the end of this post, however, obviously, ApplicationInfo is not a valid list type as there is nothing to define it with the default libraries.
Now it’s also possible that I am totally misunderstanding the use of the AndroidJavaClass function.
So I have 2 options, I think.
Option 1: create the class myself, no big deal but I don’t know what all the class contains, strings, ints, their names, etc. Nor have I been able to find that information.
Option 2: get another solution to retrieve the data I need.
Any advice/ideas/solutions?

AndroidJavaClass pluginClass = new AndroidJavaClass("android.content.pm.PackageManager");
AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject currentActivity = jc.GetStatic<AndroidJavaObject>("currentActivity");AndroidJavaObject packageManager = currentActivity.Call<AndroidJavaObject>("getPackageManager");
int flag = pluginClass.GetStatic<int>("GET_META_DATA");
AndroidJavaObject[] arrayOfAppInfo = packageManager.Call<AndroidJavaObject[]>("getInstalledApplications", flag);

BUMP! Good news, ish.
Got it working, but I have no idea how to get the variables out of the Java object and into C# strings and ints.
I’ll post what I used to get it working (its functional but not the most efficient)

AndroidJavaClass pluginClass = new AndroidJavaClass("android.content.pm.PackageManager");
AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject currentActivity = jc.GetStatic<AndroidJavaObject>("currentActivity");
int flag = pluginClass.GetStatic<int>("GET_META_DATA");
AndroidJavaObject pm = currentActivity.Call<AndroidJavaObject>("getPackageManager");
AndroidJavaObject syspackages = pm.Call<AndroidJavaObject>("getInstalledApplications", flag);

Anyone have any ideas how to extract the variables?

1 Like

bump again!
got it working for everything except drawables.

            AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
            AndroidJavaObject currentActivity = jc.GetStatic<AndroidJavaObject>("currentActivity");
            int flag = new AndroidJavaClass("android.content.pm.PackageManager").GetStatic<int>("GET_META_DATA");
            AndroidJavaObject pm = currentActivity.Call<AndroidJavaObject>("getPackageManager");
            AndroidJavaObject packages = pm.Call<AndroidJavaObject>("getInstalledApplications", flag);
            //above is working



            int count = packages.Call<int>("size");
            AndroidJavaObject[] links = new AndroidJavaObject[count];
            string[] names = new string[count];
            icons = new AndroidJavaObject[count];
            List<byte[]> byteimg = new List<byte[]>();
            int ii =0;
            for(int i=0; ii<count;){
                //get the object
                AndroidJavaObject currentObject = packages.Call<AndroidJavaObject>("get", ii );
                    try{
                    //try to add the variables to the next entry
                    links[i] = pm.Call<AndroidJavaObject>("getLaunchIntentForPackage", currentObject.Get<AndroidJavaObject>("processName"));
                    names[i] = pm.Call<string>("getApplicationLabel", currentObject);
                    icons[i] = pm.Call<AndroidJavaObject>("getApplicationIcon", currentObject);
                    Debug.Log ("(" + ii + ") " + i +" "+ names[i]);
                    //go to the next app and entry
                    i++;
                    ii++;
                    }
                catch{
                    //if it fails, just go to the next app and try to add to that same entry.
                    Debug.Log("skipped "+ ii);
                    ii++;
                }

            }

Anyone have any ideas how to get the drawables to a Texture2D or something? I’m thinking maybe transfer them to bitmap then to a byte array, transfer the array and make it back into a bitmap to use for texture2D but that’s way beyond my knowledge.

2 Likes

I’ve tried to get the package name of the installed apps using the above method. I was using “getPackageName”. but i’m not able to get a the package name. By package name i mean “com.something.something”. Any idea how to get it?

Little late of a reply, sorry.
Just a heads up: everything works except the whole icons part, the only method that I can think of that will work is converting the drawable to a byte array, then converting that to Texture2D and I have no idea how to do that, hopefully there’s a better way.
Because of that I ended up scrapping the project and moving to Android Studio for it.
So keep that stuff in mind.

Another thing to keep in mind, In the method I use the launch intent rather than the package name, primarily because there are A LOT of apps that have a package name, but no ability to launch it, while as launch intent is only ones that can be launched.
It’s compensation for ResolveInfo being inefficient through Unity from what I hear.
Booting the LaunchIntent should work using

startActivity(LaunchIntent);

Now if you want the package name specifically for whatever reason you should be able to do

mystring = pm.Call<string>("getPackageName", currentObject);

Remember that it has to go through the package manager to work, so lines 1 through 5, and line 18, are VERY important.

@Jakedude435 Hello and thanks for sharing your code! It’s great that it works without adding extra plugins to the project. Now I’m trying to modify it a bit in order to call

PackageManager.getInstallerPackageName(“com.mycompany.mygame”)

which from what I understand corresponds to:

string installer = pm.Call<string>("getInstallerPackageName", "com.mycompany.mygame");

Debug.Log(installer);

but I can’t get it to work at all. Adding this line at line 6 (before your int count = packages line) breaks the code and it just silently fails, no exceptions / errors or any other behaviour.

Can you give me any hints on why that happens / how to solve it? Cheers!

Never really got that far, got stuck at displaying the images, gave up, went to Android Studio because learning Java would be easier than fiddling with this half built interface.
I love Unity, don’t get me wrong, but this stuff is madness.
By the time I already had the working project in Android Studio and learned Java, I found out that you can just convert the drawable to a byte array, pipe that to Unity, then turn it to a texture.

Which I should point out is crazy inefficient.

That aside, I can’t say for sure why it’s having a fit if you put this at line 6, there shouldn’t be any reason for it. Unless this failing just makes the code, metaphorically, trip down the stairs.
Nor can I see reason for the code to fail, you are calling it correctly… Unless it’s just picky and wants the “com.mycompany.mygame” part as a String in an AndroidJavaObject.

It would be inefficient, but you could add this in line 25:

instalernames[i] = pm.Call<string>("getInstallerPackageName", currentObject);

then, when you want the information you could do something like:

Debug.Log(installernames[names.indexOf("My App Name")]);

Hey thanks for replying! I’ve shipped an app with Android Studio and it was a great dev experience, but since I’m practically finished with this project and all I’m missing is store detection so I’m trying to avoid adding an extra plugin just for that.

I tried your code but it silently fails as well. I also tried with

instalernames[i] = pm.Call<string>("getInstallerPackageName", new AndroidJavaObject("String", "com.tallguyproductions.agameofcoins"));

but it also accomplished nothing -_-

On the contrary my original code works when quering the installer package name of the google installer itself (“com.android.vending”).

installerPackageName = pm.Call<string>("getInstallerPackageName", "com.android.vending");

Which makes this whole mess even crazier… I am able to read the installer package name of a an app which isn’t mine but I can’t for my own app… Anyway just sharing my thoughts out loud here, I’ll go back to trying stuff!

Anyway I’ll look into it some more and maybe will end up making a plugin for it. Thanks for the info so far! :slight_smile:

Strange, if it works for vending it should work the same for your application.
Maybe it’s an address error, are you sure you are putting in the address for your application as it appears on android?
Theres an app floating around called Lucky Patcher (check around Aptoide), that can tell you the launch intent name of the package. (Plus it would let you see if your attempt to prevent piracy even works in the first place, assuming that’s your intent)

Turns out I’m just an idiot :slight_smile:

None of the other apps had an installer package name, because they were the apps which came with the phone (it’s an old android I’ve factory resetted and kept for development only). And my app was originally downloaded from Google Play’s beta feature but when I Build & Run it apparently overrides the installer name :confused: So there was no name to check for :stuck_out_tongue:

After using a custom adb install with argument -i “com.android.vending” the code did work and return the right string.

Anyhow thanks a lot for giving me a hand, best of luck to your current & future projects !! :slight_smile:

Interesting, I never would have thought of that, that will be great to keep in mind for future projects, so looks like you’ve helped both of us long run, haha. Thanks and well done.
Glad you found the problem/solution, and best of luck with your endeavors as well.

1 Like

Hello Guys,

Can any one please provide me an plugin for the same, we also want to check inside our game if the game was installed from the google play android market and not some other android market. Any help would be appreciated.

Thank you,
Raviraj.

Thank you @aliyeredon2

You can use Application.installerName

Any idea for getting icon as a sprite? I have tried to get base64 string and then decode it to byte[ ]: not working (one of the error was: invalid string length). I have tried to optain directly byte[ ] from drawable android object: not working.
Here is my code:

AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
        AndroidJavaObject currentActivity = jc.GetStatic<AndroidJavaObject>("currentActivity");
        int flag = new AndroidJavaClass("android.content.pm.PackageManager").GetStatic<int>("GET_META_DATA");
        AndroidJavaObject pm = currentActivity.Call<AndroidJavaObject>("getPackageManager");
        AndroidJavaObject packages = pm.Call<AndroidJavaObject>("getInstalledApplications", flag);
            //above is working

       
       

        int count = packages.Call<int>("size");
        string[] names = new string[count];
        List<byte[]> byteimg = new List<byte[]>();
        int ii =0;
        for(int i=0; ii<count;){
                //get the object
            AndroidJavaObject currentObject = packages.Call<AndroidJavaObject>("get", ii );
            try{
                    //try to add the variables to the next entry
                names[i] = pm.Call<string>("getApplicationLabel", currentObject);
                AndroidJavaObject icon = pm.Call<AndroidJavaObject>("getApplicationIcon", currentObject);
                AndroidJavaObject bitmap = icon.Call<AndroidJavaObject>("getBitmap");
                AndroidJavaClass stream = new AndroidJavaClass("java.io.ByteArrayOutputStream");
                bitmap.Call("compress",(new AndroidJavaClass("java.io.ByteArrayOutputStream")).Call<AndroidJavaObject>("JPEG"), 100, stream);
                byte[] bitMapData = stream.Call<byte[]>("toByteArray");
                Texture2D mytexture = new Texture2D(50, 50); // no idea what default size would be?? is it important??
                if (!mytexture.LoadImage(bitMapData)) {
                    Debug.Log("Failed loading image data!");
                }
                else {
                    Debug.Log("LoadImage - Still sane here - size: " + mytexture.width + "x" + mytexture.height);
                    GameObject app = (GameObject)Instantiate(App, Vector3.zero, Quaternion.identity);
                    app.GetComponent<RawImage>().texture = mytexture;
                }
                i++;
                ii++;
            }
            catch(Exception e){
                Debug.LogError(e,this);
                    //if it fails, just go to the next app and try to add to that same entry.
                ii++;
            }

Thanks