I don't think this is an IntelliJ issue specifically but giving all the details regardless. I created an Android Test Application in IntelliJ 13 and I'm trying to test some existing code in an Android application. This bit of code accepts an InputStream which is a JSON file to be read. I am trying to test a method for reading this file InputStream. I have the following:
My project structure looks like this:
android_test
- libs
- res
- src
- testAssetes
- file.json
And my test method is:
public void testParseFile() {
FileInputStream in = new FileInputStream(new File("./testAssets/file.json"));
// code continues..
}
I keep getting a FileNotFound exception. I've also tried the following:
android_test
- libs
- res
- src
- com
- test
- file.json
And then my code is modified to:
InputStream in = getClass().getResourceAsStream("/com/test/file.json")
And in this case, the InputStream is always null. I created a regular java project with similar code and it worked just fine. Not sure if it has something to do with how the InstrumentTestRunner is working or what the deal is.
UPDATE: I just realized that the problem is probably because these tests are running with the InstrumentationTestRunner which executes on the emulator/device. So I think what I need to know is how to get my test file on to that file system and read it in for a test.
You can specify the android_test directory as the "Working directory" in IntelliJ via "Run > Edit Configurations"
One way to do it is to use a bit of reflection. A utility method I use is :
public String assetFileContents(final String fileName) throws IOException {
StringBuilder text = new StringBuilder();
InputStream fileStream = this.getClass().getClassLoader()
.getResourceAsStream("assets/" + fileName);
BufferedReader reader = new BufferedReader(new InputStreamReader(fileStream));
String line;
while((line = reader.readLine()) != null) {
text.append(line);
}
return text.toString();
}
This return the String content of fileName. Also, I keep the test files in assets folder at /src/tests/assets (set via assets.srcDir in build.gradle, I think default has instrumentTests instead of tests)
I don't think the InstrumentTestRunner argument is relevant. As long as it's in the source path folders it should be packaged along with the app.
To see running examples using test files, see my repository here (FYI, I'm currently refactoring the project, they might have moved to another branch at a later point).
Related
Premise
Soon I will have to update my app from android 10 to android 11.
compileSdkVersion 30
targetSdkVersion 30
I already had in the previous version
android: requestLegacyExternalStorage = "true" (Documentation says this will be ignored on android 11)
I already had in the previous version the FileProvider definition and so I left it as is.
Everything works perfectly on ANDROID 10.
Now,
From the tests carried out on the emulator (Android 11) my app is still able to create the "my custom directory" in "download / myappname" without problems even though I have the target at SDK 30... but hadn't this been prevented?
And since it allows me to do this, why then does EACCES (Permission denied) give me an error if I try to read a file which is inside the same (external) directory created by my own app?
The folder wasn't even there before, the app created it ... what's the point of preventing it from reading a static file inside it?
isn't that a contradiction?
How did you go about adapting to these limitations that I think have been adopted only in part given my practical results ... what am I missing?
Can anyone explain to me what is happening?
For example this still seems to work on Android11:
String PublicDirAccess = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString();
//create new file directory object
directory = new File(PublicDirAccess + "/" + UtiGlobal.getAppTitle(MyApp.getContext()) + "/");
// if no directory exists, create new directory
if (!directory.exists())
{
directory.mkdir();
//MediaScannerConnection.scanFile(MyApp.getContext(), new String[] {directory.getAbsolutePath()}, null, null);
forceMediaScanner(MyApp.getContext(), directory);
}
But reading the documentation shouldn't work ... it shouldn't create the folder ... or not?
But then if I try to read a file inside it like this:
BufferedReader reader = new BufferedReader(new InputStreamReader(mystreamInDownloadFolder));
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null)
{
sb.append(line).append("\n");
}
reader.close();
StringOut= sb.toString();
It gives me the error:
EACCES (Permission denied)
But if it makes me create the folder why then it prevents me from reading in it? ... wouldn't it have been more right that the app could continue reading in a folder created by its code? Even if it is in the external directory.
My question is simple:
Is this normal? I know very well that this should no longer be allowed but I don't understand why then the folder is created.
I add how to replicate the problem even more simply:
1) Create a folder in /storage/emulated/0/Download/Test
2) Create inside a test.txt file from the app
3) Modify the content of the file via usb PC, NETWORK or from Android using X-plore, Es-explorer, etc.
You just lost access to that file! ... so it will be impossible to transfer the files generated by my app as backups, csv, text files from one device to another? ... but is this normal?
I have some level definitions in xml format (with .txt extension) inside (without any subfolders) my project's rescources folder
I for more scalability, I have a plain text file naming all these level definition XMLs
I read that file using
TextAsset WorldList = (TextAsset)Resources.Load("WorldList");
And then I load the needed world:
TextAsset TA = (TextAsset)Resources.Load(/*"LevelDefs/" +*/ Worlds[worldToload]);
xps.parseXml(TA.text);
loadLevel(levelToLoad);
(you see that I have moved these rescources out of subfolder to reduce the chance of them not loading)
worldToload here is the index number of that world
program works fine on windows but nothing loads on my android test device.
I seem to have problems debugging on device so I only guess something went wrong in loading phase.
any suggestions?
From Unity Documentation:
Most assets in Unity are combined into the project when it is built.
However, it is sometimes useful to place files into the normal
filesystem on the target machine to make them accessible via a
pathname. An example of this is the deployment of a movie file on iOS
devices; the original movie file must be available from a location in
the filesystem to be played by the PlayMovie function.
Any files placed in a folder called StreamingAssets in a Unity project
will be copied verbatim to a particular folder on the target machine.
You can retrieve the folder using the Application.streamingAssetsPath
property. It’s always best to use Application.streamingAssetsPath to
get the location of the StreamingAssets folder, it will always point
to the correct location on the platform where the application is
running.
So below snippet should work:
// Put your file to "YOUR_UNITY_PROJ/Assets/StreamingAssets"
// example: "YOUR_UNITY_PROJ/Assets/StreamingAssets/Your.xml"
if (Application.platform == RuntimePlatform.Android)
{
// Android
string oriPath = System.IO.Path.Combine(Application.streamingAssetsPath, "Your.xml");
// Android only use WWW to read file
WWW reader = new WWW(oriPath);
while ( ! reader.isDone) {}
realPath = Application.persistentDataPath + "/Your"; // no extension ".xml"
var contents = System.IO.File.ReadAll(realPath);
}
else // e.g. iOS
{
dbPath = System.IO.Path.Combine(Application.streamingAssetsPath, "Your.xml");
}
Thanks to David I have resolved this problem.
for more precision I add some small details:
1-As David points out in his answer (and as stated in unity documentations), we should use StreamingAssets if we want to have the same folder structure. One should use Application.streamingAssetsPathto retrieve the path.
However, files are still in the apk package in android's case, so they should be accessed using unity android's www class.
a good practice here is to create a method to read the file (even mandatory if you are going to read more than one file)
here is one such method:
private IEnumerator LoadDataFromResources(string v)
{
WWW fileInfo = new WWW(v);
yield return fileInfo;
if (string.IsNullOrEmpty(fileInfo.error))
{
fileContents = fileInfo.text;
}
else
fileContents = fileInfo.error;
}
This is a coroutine, and to use it we need to call it using
yield return StartCoroutine(LoadDataFromResources(path + "/fileName.ext"));
Yet another remark: we can not write into this file or modify it as it's still inside the apk package.
It is similar to this question, however my problem is that the images are there in Codename ONE simulator (image can be loaded and shows correctly), but when I do an android build and try it on my phone, I get a java.io.FileNotFoundException. According to the comment given by Shai Almog to the question mentioned above, it should work.
What is wrong here? Could it be filenames have to be all lower case or all upper case?
(I am omitting the source here because it seems to be a problem with packaging rather than the code itself.)
UPDATE: I tried changing all filenames to lowercase but to no avail.
Finally I found out that you have to open the files as resources (Thanks to #ChrisStratton for pointing in the right direction whilst the comment was obviously about android SDK development).
This works:
private static Image loadImage(String path) throws IOException {
InputStream in=null;
try {
// works in simulator but not on device:
// in = FileSystemStorage.getInstance().openInputStream(path);
// works on both simulator and device:
in = Display.getInstance().getResourceAsStream(null, path);
return Image.createImage(in);
} finally {
if (in!=null) {
in.close();
}
}
}
You should create Images with EncodedImage.create("/fileName") or better yet add them to the res file where you can use the multi image format: http://www.codenameone.com/how-do-i---fetch-an-image-from-the-resource-file---add-a-multiimage.html
The approach with the input stream is slightly less efficient than what we can do internally.
Files in the src directory are implicitly placed in the assets directory for Android in Codename One so there is no need for an assets directory.
Good afternoon everyone,
I'm having a problem here with an CSV file that I wanna use to fill a dynamic table in Android.
I placed the file in the "data" folder of my emulator, and I did the following declaration to create my file variable:
file = new File("/data/Motors.csv");
I used a CSVReader class posted on another thread from this page (Link to the class) and also declared the following line to do the parse:
readerCSV = new CSVReader(new FileReader(file),';','"',0);
In the last line, the programmer to be able to create the variable has to send as parameters a Reader (Or a FileReader, it doesn't make a difference), the separator char, the quote char and the number of how many lines the CSV reader has to skip.
The problem comes that no matter in which directory (Not even in the root directory, which Java tells me that it's "/" through the file.getAbsolutePath(); method) it always gets to the point when the program throws the FileNotFoundException and I'm getting a bit frustrated because I have 2 days already with this problem.
If someone could please help me to get a bit of orientation of what I should do to get the file from the right directory... It would be a lot of help and I can go further with the code I have to finish.
Many thanks in advance!!!!
EDIT
I found a solution to open my file:
I have used 2 file variables: one gets the rout of the External Storage Directory (see first answer in the following link) and the other is declared as a new file, like the following lines in the code:
route = Environment.getExternalStorageDirectory();
file = new File(route,"Motors.csv");
Later I used again the CSV reader class that I found, where the first parameter is from the FileReader type (Sorry #Rajesh, but I couldn't use your alternative because you passed a FileDescription parameter from the AssetManager to the CSV reader class and then I had to change the whole class and that didn't help my problem at all).
Now I don't get the exception at all!!
There are various Storage Options in Android. Files can be stored in internal and external storages. If the CSV file is a static resource, you could think of bundling the file in the "assets" folder of your project.
If you are using the "assets" route, the following code segment can get you a CSVReader.
AssetManager am = getAssets();
AssetFileDescriptor afd = am.openFd ("Motors.csv");
FileDescriptor fd = afd.getFileDescriptor();
readerCSV = new CSVReader(fd, ';','"',0);
The above code snippet doesn't perform error/exception handling, please remember to take care of that. You need to copy the Motors.csv file to the "assets" folder within the project.
I package a text file with my Android App (in Assets) which I read within the App itself.
To avoid this file being compressed, it's named 'mytestfile.mp3' and until recently, that worked just fine.
In one of the recent SDK/ADT changes, it seems something 'odd' is happening when reading from Assets and I'm open to ideas as to what it is...
I use code something like this
AssetFileDescriptor descriptor = getAssets().openFd("mytextfile.mp3");
BufferedReader f = new BufferedReader(new FileReader(descriptor.getFileDescriptor()));
String line = f.readLine();
while (line != null) {
// do stuff
Log.d("TAG",line);
}
What I'm now seeing from the Log is rather odd - if the file contained something like this
Fred
Barney
Wilma
I'm seeing huge amounts of nonsense like this in the log
��ߴ�!�c�W���6�f����m�>ߩ���'�����6�#6���l0��mp�
followed - eventually by my text content
Fred
Barney
Wilma
followed by another metric tonne of gibberish - some of which looks like this
����������4�u?'����������������������������������������res/drawable-mdpi/icon.pngPK��������|v?,������������'�����������������������������res/layout-land/dialog_color_picker.xmlPK��������|v?1�!�����t2�������������������������������classes.dexPK��������|v?թVڝ����5���������������������������������META-INF/MANIFEST.MFPK��������|v?�v������j���������������������������������META-INF/CERT.SFPK��������|v?W7#�]�������������������������������������META-INF/CERT.RSAPK������������������������
As you can see, that appears to be raw binary content from the APK (and nothing to do with the text file)??
Is this a recent packaging issue or am I missing something? I'm using ADT15 but I've not tried the recent upgrade just yet!?
p.s. I've upgraded to the latest SDK/ADT and this problem persists - obviously I'd like to escalate it with whoever is at fault (no idea if the problem is Eclipse/ADT/ANT or Android centered) and so I'll start a bounty for ideas...
This is because AssetFileDescriptor.getFileDescriptor() is for your .apk and not the mytextfile.mp3 file inside the .apk. To work with AssetFileDescriptor you need to take e.g. AssetFileDescriptor.getStartOffset() into account as well, which is the offset to the actual file i.e. mytextfile.mp3 in your case.
But there's an easy solution to your problem. Use AssetManager.open(String) instead, which will give you an InputStream to the mytextfile.mp3 file. Like this:
InputStream inputStream = getAssets().open("mytextfile.mp3");
BufferedReader f = new BufferedReader(new InputStreamReader(inputStream));
// ...
Eclipse/ADT occasionally gets the resources corrupted. Try doing a project clean and rebuild to see if that fixes it.
I had the same problem with my app. Try using Apache Commons IO's FileUtils.
This adds another 100kb to your apk, but make File handling much easier.
And if you store the file as myfile.txt instead of .mp3, does it give the same output?
And did you create the file with a Windows or Linux/Unix System? (And with what application?)
/edit: This works for me:
AssetManager am = this.getAssets();
InputStream is = am.open("mytextfile.mp3");
InputStreamReader inputStreamReader = new InputStreamReader(is);
BufferedReader f = new BufferedReader(inputStreamReader);
String line = f.readLine();
while (line != null) {
// do stuff
Log.d("TAG", line);
line = f.readLine();
}