- Home /
Convert Android URI to a file path Unity can read
I have an external app sending me a URI of a .zip file I need to access. Here is my attempt to convert the uri:
AndroidJavaClass UnityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject currentActivity = UnityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
AndroidJavaObject intent = currentActivity.Call<AndroidJavaObject>("getIntent");
AndroidJavaObject data = intent.Call<AndroidJavaObject>("getData");
AndroidJavaObject fileObject = new AndroidJavaObject("java.io.File", data);
Debug.Log(fileObject.Call<string>("getPath"));
I am getting the uri correctly. But the constructor for the File class is getting this error:
UnityEngine.AndroidJavaException: java.lang.NoSuchMethodError: no non-static method "Ljava/io/File;.<init>(Landroid.net.Uri$HierarchicalUri;)V"
05-08 16:26:54.997 30011 30026 I Unity : java.lang.NoSuchMethodError: no non-static method "Ljava/io/File;.<init>(Landroid.net.Uri$HierarchicalUri;)V"
How do I pass the uri to the java File class so I can get the proper path to the zip file?
Thanks for the help!
Have you had any luck with this? I'm encountering a similar problem.
There are 2 different URI classes. One for java, one for android. The File class uses the java uri, while I was passing it an android uri.
The only way I could figure out how to get the file from the android uri is with an InputStream. But the function was very slow in Unity because it had to be called a bunch of times to get all the bytes. I ended up writing a Java plugin that grabs all the bytes from the file and passes the byte array to Unity.
So I found a way to work around this. What I'm doing is using the URI to read the file as an input stream. By doing that you can read the file byte by byte, or wrap the input reader in something more sophisticated extract strings. In the absolute laziest case you could read all the bytes to memory, write them out to a known file location and then read that file as you normally would. That code would look something like this;
AndroidJavaClass unityPlayerClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject activityObject = unityPlayerClass.GetStatic<AndroidJavaObject>("currentActivity");
AndroidJavaObject intent = activityObject.Call<AndroidJavaObject>("getIntent");
AndroidJavaObject intentURI = intent.Call<AndroidJavaObject>("getData");
AndroidJavaObject contentResolver = activityObject.Call<AndroidJavaObject>("getContentResolver");
AndroidJavaObject inputStream = contentResolver.Call<AndroidJavaObject>("openInputStream", intentURI);
List<byte> bytes = new List<byte>();
int nextByte = inputStream.Call<int>("read");
while (nextByte != -1)
{
bytes.Add((byte)nextByte);
nextByte = inputStream.Call<int>("read");
}
inputStream.Call("close");
File.WriteAllBytes(my$$anonymous$$nownPath, bytes.ToArray());
$$anonymous$$yFileReadingFunction(my$$anonymous$$nownPath);
Damn, just saw your reply where you said you ended up doing the same thing!... until it was too slow for you. I'll leave this up here though, in case it's useful for anyone.
Answer by liortal · Jun 05, 2017 at 06:48 AM
Your original code is fine, but this is a limitation with how JNI finds and invokes methods from C# (well, technically from C++, but Mono is using the same infrastructure).
See the following (related) question on Stack overflow.
The problem is you're trying to find a method that has a signature that is different than the actual File class constructor, and so it fails.
The easiest solution you can do is create a simple plugin (fancy name for a small .jar containing helper methods), then call these Java helper methods from your C# code to achieve what you want.
Answer by Guy-Corbett · Jun 05, 2017 at 05:21 PM
So found a way to do this without writing a plugin or resorting to reading byte by byte. Seems to run pretty fast. Here's the function I've written which copies the whole of any file given with the intent to another location;
private void ImportFromIntent(string importPath)
{
try
{
// Get the current activity
AndroidJavaClass unityPlayerClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject activityObject = unityPlayerClass.GetStatic<AndroidJavaObject>("currentActivity");
// Get the current intent
AndroidJavaObject intent = activityObject.Call<AndroidJavaObject>("getIntent");
// Get the intent data using AndroidJNI.CallObjectMethod so we can check for null
IntPtr method_getData = AndroidJNIHelper.GetMethodID(intent.GetRawClass(), "getData", "()Ljava/lang/Object;");
IntPtr getDataResult = AndroidJNI.CallObjectMethod(intent.GetRawObject(), method_getData, AndroidJNIHelper.CreateJNIArgArray(new object[0]));
if (getDataResult.ToInt32() != 0)
{
// Now actually get the data. We should be able to get it from the result of AndroidJNI.CallObjectMethod, but I don't now how so just call again
AndroidJavaObject intentURI = intent.Call<AndroidJavaObject>("getData");
// Open the URI as an input channel
AndroidJavaObject contentResolver = activityObject.Call<AndroidJavaObject>("getContentResolver");
AndroidJavaObject inputStream = contentResolver.Call<AndroidJavaObject>("openInputStream", intentURI);
AndroidJavaObject inputChannel = inputStream.Call<AndroidJavaObject>("getChannel");
// Open an output channel
AndroidJavaObject outputStream = new AndroidJavaObject("java.io.FileOutputStream", importPath);
AndroidJavaObject outputChannel = outputStream.Call<AndroidJavaObject>("getChannel");
// Copy the file
long bytesTransfered = 0;
long bytesTotal = inputChannel.Call<long>("size");
while (bytesTransfered < bytesTotal)
{
bytesTransfered += inputChannel.Call<long>("transferTo", bytesTransfered, bytesTotal, outputChannel);
}
// Close the streams
inputStream.Call("close");
outputStream.Call("close");
}
}
catch (System.Exception ex)
{
// Handle error
}
}
Your answer
Follow this Question
Related Questions
Open another app with intent url on Android 1 Answer
External Saves for android games 1 Answer
Faster way to Pass data from C# to Java (Android) 1 Answer
android file open 0 Answers
Allow … to access photos, media, and files on your device? 2 Answers