javaandroidunity-game-engine

How to get Android Installed App Icon in Unity


Quickly, I want to get all the installed apps in the device (icon and label). I'm using a simple code that works getting the name of the app.

I use Android Java Class to get these informations. The problem is that Android optains app icon as a Drawable. Unity cannot read and display it as Sprite.

I am wondering if there is a way to sort this issue out. I have tried to encode the drawable to base64 string but Unity "replies" me with a "Invalid string length" error, maybe for the "infinite" length of the base64 string.

I am trying to convert the Drawable to a byte array and then use it to create a texture with Texture.loadImage(byte[]) but it doesn't work. Here is the 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);

    int count = packages.Call<int>("size");
    string[] names = new string[count];
    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);//this part is the Java (Android Studio) code using Android Java Object and Class of Unity. Maybe the error is from here
            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");//to here
            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;//here is the code should display the icon as texture (sprite would be the best)
            }
            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++;
        }

    }

Here there is the Java Android Studio working code:

        Bitmap bitmap = ((BitmapDrawable) item.getIcon()).getBitmap(); //item.getIcon() returns the Drawable correctly
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
        byte[] byteArray = baos.toByteArray();

Solution

  • I have created an Android Jar Plugin on Android Studio (No Activity) and then I have imported it in Unity3D (AndroidManifest.xml and classes.jar in Assets>Plugins>Android>libs). Inside the plugin I have added a class and then the void to get the byte[] of the icon. Java Android Plugin Class:

    public class PluginClass {
    
    public static byte[] getIcon(PackageManager pm, ApplicationInfo applicationInfo) {
        try {
            BitmapDrawable icon = (BitmapDrawable) pm.getApplicationIcon(applicationInfo);
            Bitmap bmp = icon.getBitmap();
            ByteArrayOutputStream stream = new ByteArrayOutputStream();
            bmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
            byte[] byteArray = stream.toByteArray();
            return byteArray;
        } catch (Exception e) {
            return null;
        }
    }
    
    public static boolean isSystem(ApplicationInfo applicationInfo){
        return (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM ) != 0;
       }
    }
    Then I have invoked it in Unity C# script:
    
    void Start () {
        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", 0);
        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;){
            AndroidJavaObject currentObject = packages.Call<AndroidJavaObject>("get", ii );
            try{
                names[i] = pm.Call<string>("getApplicationLabel", currentObject);
                var plugin = new AndroidJavaClass("com.mypackagename.PluginClass");
                if(plugin.CallStatic<bool>("isSystem",currentObject)){
                    ii++;
                    continue;
                }
                byte[] decodedBytes = plugin.CallStatic<byte[]>("getIcon", pm, currentObject);
                Texture2D text = new Texture2D(1, 1, TextureFormat.ARGB32, false);
                text.LoadImage(decodedBytes);
                Sprite sprite = Sprite.Create (text, new Rect(0,0,text.width,text.height), new Vector2(.5f,.5f));
                i++;
                ii++;
            }
            catch(Exception e){
                Debug.LogError(e,this);
                ii++;
            }
    
        }