简体   繁体   中英

How to get variables from AndroidJavaObject into a C# class using Unity3D

I can't find how to get ListArray variables from an AndroidJavaObject in C#.
I'm trying to make a for function using a Count for the number of items in the ListArray that is stored as an AndroidJavaObject . But I need to know how to get the Count from the AndroidJavaObject and how to use it properly.
This is what I'm using to get the variables, also notice that "packages" is not an AndroidJavaObject[] .

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);

it's very rudimentary at this point, but it works thanks to some help from this How to get installed apps list with Unity3D?

The progress thus far stalls at getting the icon, everything else works perfect, I need a way to get either a string that links to the icon somehow, or the Texture2D of the icon.
Another alternative would be a to use a AndroidJavaObject that contains a drawable as if it's a Texture2D. I have no idea how to accomplish any of this though.
Another Idea I had was to convert it to another variable, like byte[] that can be transferred and converted back, but I have yet to find a method of that which works under the circumstance.

int count = packages.Call<int>("size");
AndroidJavaObject[] links = new AndroidJavaObject[count];
string[] names = new string[count];
Texture2D[] icons = new Texture2D[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
        links[i] = pm.Call<AndroidJavaObject>("getLaunchIntentForPackage", currentObject.Get<AndroidJavaObject>("processName"));
        names[i] = pm.Call<string>("getApplicationLabel", currentObject);
        icons[i] = pm.Call<Texture2D>("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++;
    }

}

I really hate to ask two questions in a row here, and I apologize for having to do so, but this is a difficult and seemingly awkward circumstance, that has little to no documentation (that I can find).

First of all the docs on the interop between Unity3D and Android are scarce at best.

The most important thing is that the only interface Unity exposes to work with java object is AndroidJavaObject and that interface has only a couple of methods defined . You can only use those ones and only those.

This means that you don't get a Count object when working with an java array and you'll still be working with AndroidJavaObject .

int count = packages.Call<int>("size"); //getting the size of the array

for( int i = 0; i < count; i++ )
{
 //getting the object at location i
 AndroidJavaObject currentObject = packages.Call("get", i );

 //use currentObject using the same methods as before: Call, CallStatic, Get, GetStatic
}

I know this is verbose, and writing code like this is hard to test, you need to make and apk and deploy it to a device to check that it runs.

Probably a faster way of doing this is to make your own java class that does all this on the java side and expose one method that gets called from the Unity side. This way at least you get the benefit of static typing when making a jar that you'll add in the Plugins/Android folder.

I'm trying to do the same thing here, but my launcher must only show the CardBoard apps. What i've decided is to make a library in java and import it to Unity as a plugin:

This is my Java class:

public class Launcher extends UnityPlayerActivity {

private List<ApplicationInfo> cbApps;

@Override
protected void onStart() {
    super.onStart();

    PackageManager pm= getPackageManager();
    List<ApplicationInfo> lista = pm.getInstalledApplications(PackageManager.GET_META_DATA);
    cbApps= new LinkedList<ApplicationInfo>();

    for(ApplicationInfo ai : lista){
        Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
        intentToResolve.addCategory("com.google.intent.category.CARDBOARD");
        intentToResolve.setPackage(ai.packageName);
        if(pm.resolveActivity(intentToResolve, 0)!=null) {
            cbApps.add(ai);
        }
    }
}


public int getListSize(){
    return cbApps.size();
}

And here my C# method:

void Start () {
    AndroidJavaClass unity = new AndroidJavaClass ("com.unity3d.player.UnityPlayer");
    AndroidJavaObject currentActivity = unity.GetStatic<AndroidJavaObject> ("currentActivity");
    texto.text=""+currentActivity.Call<int> ("getListSize");

}

This way I can create in Java every method that I need to acces the list. The problem I'm still having is trying to get the Texture2D of the icons. I've tried returning the Drawable from Java and accesing with a Call just as you did, but it doesn't work at all. I've been 2 days working with that, I'll let you know if I find a solution.

EDIT: Finally I could get the images:

First in Java I created this method:

public byte[] getIcon(int i) {
        BitmapDrawable icon= (BitmapDrawable)context.getPackageManager().getApplicationIcon(cbApps.get(i));
        Bitmap bmp = icon.getBitmap();
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        bmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
        byte[] byteArray = stream.toByteArray();
        return byteArray;
    }

That way I can access the Byte array representing the Drawable from Unity and show it as the main texture of a Plane:

void Start () {
    using (AndroidJavaClass unity = new AndroidJavaClass ("com.unity3d.player.UnityPlayer")) {
        context = unity.GetStatic<AndroidJavaObject> ("currentActivity");
    }
    using (AndroidJavaClass pluginClass=new AndroidJavaClass("com.droiders.launcher.Launcher")) {
        launcher= pluginClass.CallStatic<AndroidJavaObject>("getInstance", context);
        byte[] bytes= launcher.Call<byte[]>("getIcon",0);
        Texture2D tex= new Texture2D(2,2);
        tex.LoadImage(bytes);
        plane.GetComponent<Renderer>().material.mainTexture=tex;
    }

}

Hope it helps you. It's been hard but this beast has been tamed :P Also thanks you all for your ideas.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM