File acess on Android App when using Android NDK - android

I have a native shared library compiled using android-ndk
one of the C API functions is:
int load_config(const char *fn, Config *cfg)
I have wrapped the function with JNI and successfuly called it from Java, e.g.
Config cfg = new Config();
my_shared_lib.load_config("test.gcf", cfg);
System.out.println("cfg.rx_id = " + cfg.getRx_id());
This prints the expected value from the Config data structure
But now that I have ported it on to an Android emulator I am confused as how to handle the string representing the filename of the configuration data
I have tried adding test.gcf to the $(PROJECT)/assets dir then:
Config cfg = new Config();
my_shared_lib.load_config("assets/test.gcf", cfg);
outputText.append("cfg.rx_id = " + cfg.getRx_id());
but this doesn't work, i.e. the expected value from the Config data structure isn't ouput (I just get a blank)
In other words, what do you do when your native library function expects a filename as a parameter?

Assets are not stored as files, neither on the device nor on the emulator. They're stored in the APK, which is a ZIP archive; using C/C++ file access functions won't ever work on them. For access to assets from JNI code, use AAssetManager.
Now, the config file probably does not belong to the assets. Assets are by definition read-only; you want your config to be changeable, right? The writeable data folder on Android is retrieved on the Java side via the Context.getDir() function; you derive your filename from that, and pass it to the C side. Something like this:
File Path = Ctxt.getDir("MyData", 0);
File FileName = new File(Path, "test.gcf");
my_shared_lib.load_config(FileName.toString(), cfg);
The said folder would map to the following path in the Android filesystem:
/data/data/com.mypackagename/app_MyData . Different if moving the app to an SD card is enabled.
If you want to ship a default config file in assets but keep it as writeable in the data folder as the app runs, you just have to copy it from assets to the data folder on the first run. Example here.

Related

Writing a file to a phone's internal storage with Xamarin [duplicate]

I want to save my logs to a folder which I can access with windows explorer. For example I want to create my log in the following path
This PC\Galaxy A5 (2017)\Phone\Android\data\MyApp\files
So I tried to use Environment variables... I get such as
/data/user/...
But here i cannot see the file what I created (using code I can access the path but I want to see in the explorer).
how I can create a path like above with code?
When I tried this code
var finalPath2 = Android.OS.Environment.GetExternalStoragePublicDirectory
(Android.OS.Environment.DataDirectory.AbsolutePath);
I get the path "/storage/emulated/0/data"
and
If i use the code
var logDirectory =Path.Combine(System.Environment.GetFolderPath
(System.Environment.SpecialFolder.ApplicationData),"logs");
I get the following path like:
/data/user/0/MyApp/files/.config/logs
and
var logDirectory =Path.Combine(System.Environment.GetFolderPath
(System.Environment.SpecialFolder.MyDocuments),"logs");
"/data/user/0/IM.OneApp.Presentation.Android/files/logs"
but unfortunately I cannot access this folder by explorer....
This PC\Galaxy A5 (2017)\Phone\Android\data\MyApp\files
So how to find out this path in c# by using environments?
Update:
when I give the following path hardcoded, it creates the file where I want..
logDirectory = "/storage/emulated/0/Android/data/MyApp/files/logs";
is there any environment to create this path? I can combine 2 environments and do some string processing in order to create this path. But maybe there is an easier way?
You are looking for the root of GetExternalFilesDir, just pass a null:
Example:
var externalAppPathNoSec = GetExternalFilesDir(string.Empty).Path;
Note: This is a Context-based instance method, you can access it via the Android application context, an Activity, etc... (see the link below to the Android Context docs)
Shared storage may not always be available, since removable media can be ejected by the user. Media state can be checked using Environment.getExternalStorageState(File).
There is no security enforced with these files. For example, any application holding Manifest.permission.WRITE_EXTERNAL_STORAGE can write to these files.
re: https://developer.android.com/reference/android/content/Context#getExternalFilesDir(java.lang.String)
string docFolder = Path.Combine(System.Environment.GetFolderPath
(System.Environment.SpecialFolder.MyDocuments), "logs");
string libFolder = Path.Combine(docFolder, "/storage/emulated/0/Android/data/MyApp/files/logs");
if (!Directory.Exists(libFolder))
{
Directory.CreateDirectory(libFolder);
}
string destinationDatabasePath = Path.Combine(libFolder, "temp.db3");
db.Backup( destinationDatabasePath, "main");

How to correctly load XML files on mobile devices using unity3d

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.

Get preloaded file in android

I'm new to android development and I am working on a little project. What I am having some issue with is getting access to preloaded files.
In my app, I have an XML file that I preloaded (I just simply put it in my src folder in a package). How do I access them in my classes? I need to get a File object pointing to this file so that I can use it as I would I/O files. It seems like this should be trivial, but alas I am stuck.
Lets say the file is located under: com.app.preloadedFiles/file1.XML
I've tried something along the lines of this, but have had no success:
URL dir_url = ClassLoader.getSystemResource("preloadedFiles/file1.XML");
FIle file = new File(dir_url.toURI());
I solved this in my app by getting an InputStream to the file -- something like:
myContext.getAssets().open(fileName);
//read the data and store it in a variable
Then, if you truly need to do File related opterations with it, you can write it to a private (or public) directory and do your operations from you newly written file. Something like:
File storageDir = myContext.getDir(directoryName, Context.MODE_PRIVATE);
File myFile = new File(storageDir + File.separator + fileName);
//then, write the data to the file and manipulate it -- store the name for access via File later

android data save directories

My application is mostly c++ (using NDK) so I use fopen, fwrite, etc. standard functions to create and game save files and write into them.
When I use fopen("game.sav", "wb"), it appears that it's being created at path
/data/user/10/com.my.game/files/game.sav.
My app is multi-user. So I want to have a separated folders where users store their save-files. And instead of the path above I'd like to have paths like
/data/user/10/com.my.game/files/user0/game.sav,
/data/user/10/com.my.game/files/user1/game.sav, etc
My app's frontend is in Java, and when new user is being registered, I want to create a folder /data/user/10/com.my.game/files/user0/. But I don't know how to do it, because
final File newDir = context.getDir("user0", Context.MODE_PRIVATE);
results in path being created at /data/user/10/com.my.game/app_user0 that's a different path.
It is possible to create folders at /data/user/10/com.my.game/files/ and how ?
Simple way to do it, this code you can change it suit many conditions. If you know that your path is different from what getFilesDir() gets you then you can create a File first of all by using a path that you know and the last 2 lines of code will still be same.
File file = this.getFilesDir(); // this will get you internal directory path
Log.d("BLA BLA", file.getAbsolutePath());
File newfile = new File(file.getAbsolutePath() + "/foo"); // foo is the directory 2 create
newfile.mkdir();
And if you know the path to "files" directory:
File newfile2 = new File("/data/data/com.example.stackoverflow/files" + "/foo2");
newfile2.mkdir();
Both code works.
Proof of Working:

Access Android APK Asset data directly in c++ without Asset Manager and copying

I'm using pure C++ in my engine to create a game engine in android. There is no single java file. Basically it is a game which should only be stored to external memory. When I move my asset data manually via adb to my external sd card the game works already fine and stable.
adb push ..\..\Bin\Data /sdcard/Android/data/com.fantasyhaze.%SMALL_PACKAGE_NAME%/files/Data/
This is not a good solution because it can not be delivered. Therefore I have my assets-data in Assets folder
which gets moved into the apk file during building process with the following structure:
Assets/Data/MoreFolders/Withsubfolders
Assets/Data/EngineData.zip
Assets/Data/ScriptData.zip
But I don't know where those files are on the file systems to access them in c++ code.
So I tried to get the path to the file directories. And because of a bug in the native activity state
I have to retrieve the information in normal code.
// bug in 2.3 internalDataPath / externalDataPath = null using jni code instead
//FHZ_PRINTF("INTERNAL inter PATH = %s\n", state->activity->internalDataPath);
//FHZ_PRINTF("EXTERNAL inter PATH = %s\n", state->activity->externalDataPath);
c++ code for its equivalent to android.os.Environment.getFilesDir()
and android.os.Environment.getExternalStorageState() ect
// getPath() - java
JNIEnv *jni_env = Core::HAZEOS::GetJNIEnv();
jclass cls_Env = jni_env->FindClass("android/app/NativeActivity");
jmethodID mid_getExtStorage = jni_env->GetMethodID(cls_Env, "getFilesDir","()Ljava/io/File;");
jobject obj_File = jni_env->CallObjectMethod( gstate->activity->clazz, mid_getExtStorage);
jclass cls_File = jni_env->FindClass("java/io/File");
jmethodID mid_getPath = jni_env->GetMethodID(cls_File, "getPath","()Ljava/lang/String;");
jstring obj_Path = (jstring) jni_env->CallObjectMethod(obj_File, mid_getPath);
const char* path = jni_env->GetStringUTFChars(obj_Path, NULL);
FHZ_PRINTF("INTERNAL PATH = %s\n", path);
jni_env->ReleaseStringUTFChars(obj_Path, path);
// getCacheDir() - java
mid_getExtStorage = jni_env->GetMethodID( cls_Env,"getCacheDir", "()Ljava/io/File;");
obj_File = jni_env->CallObjectMethod(gstate->activity->clazz, mid_getExtStorage, NULL);
cls_File = jni_env->FindClass("java/io/File");
mid_getPath = jni_env->GetMethodID(cls_File, "getAbsolutePath", "()Ljava/lang/String;");
obj_Path = (jstring) jni_env->CallObjectMethod(obj_File, mid_getPath);
path = jni_env->GetStringUTFChars(obj_Path, NULL);
FHZ_PRINTF("CACHE DIR = %s\n", path);
jni_env->ReleaseStringUTFChars(obj_Path, path);
// getExternalFilesDir() - java
mid_getExtStorage = jni_env->GetMethodID( cls_Env,"getExternalFilesDir", "(Ljava/lang/String;)Ljava/io/File;");
obj_File = jni_env->CallObjectMethod(gstate->activity->clazz, mid_getExtStorage, NULL);
cls_File = jni_env->FindClass("java/io/File");
mid_getPath = jni_env->GetMethodID(cls_File, "getPath", "()Ljava/lang/String;");
obj_Path = (jstring) jni_env->CallObjectMethod(obj_File, mid_getPath);
path = jni_env->GetStringUTFChars(obj_Path, NULL);
FHZ_PRINTF("EXTERNAL PATH = %s\n", path);
jni_env->ReleaseStringUTFChars(obj_Path, path);
//getPackageCodePath() - java
mid_getPath = jni_env->GetMethodID(cls_Env, "getPackageCodePath", "()Ljava/lang/String;");
obj_File = jni_env->CallObjectMethod(gstate->activity->clazz, mid_getPath);
obj_Path = (jstring) jni_env->CallObjectMethod(obj_File, mid_getPath);
path = jni_env->GetStringUTFChars(obj_Path, NULL);
FHZ_PRINTF("Looked up package code path = %s\n", path);
which works quite well and results in
INTERNAL PATH = /data/data/com.fantasyhaze.rememory/files
CACHE DIR = /data/data/com.fantasyhaze.rememory/cache
EXTERNAL PATH = /mnt/sdcard/Android/data/com.fantasyhaze.rememory/files
Looked up package code path = /mnt/asec/com.fantasyhaze.rememory-2/pkg.apk
But there are no files from the assets folders...
and I need to access a folder as normal working directory to read the files. which would be possible in
/mnt/sdcard/Android/data/com.fantasyhaze.rememory/files/Data
But moving all data from the asset folder (wherever it is) via asset manager to this folder, causes doubled consumption of memory.
assets >1GB would mean assets >2GB which doesn't make sense. More than that the assert folder seems not to work recusively and only for small data files
which is not possible when using bigger pak files. Maybe the files can be access directly from the apk when using
the unzip system and then uzip my ressource files, but therefore I have to optain the apk path anyway.
So My questions:
Where is the Assets folder in the apk on the file system?
What would be the code (c++) to retrieve the apk location or the location of the executable
Can I access it directly with a normal file open method or only if I unpack it. If I can use it without unpacking, how?
What would be the code (c++) to retrieve the information if the sd card is mounted?
I hope someone can help me :)
Edit: Added cache directory and the package directory code (and it's output paths) , to provide the source for everyone else who needs it.
I'm posting an answer instead of marking this as a duplicate because technically, you're asking for more details than are provided in the question/answer I'm going to link here. So, to answer the first of your 4 points, see Obtaining the name of an Android APK using C++ and the NativeActivity class.
As for checking to see if the SD card is mounted, that should be rather simply. Simply attempt to open the directory or a file on the SDcard (that you know should be there) and if it fails, you know the SD card is not available. See What's the best way to check if a file exists in C? (cross platform).
You may want to have a look at "Expansion Files" see APK Expansion Files.
As far is i understand this, this gives you a system that automatically puts resource files (your expansion file) on the "shared storage location".
So i think that this would solve your problem of duplicated data.
There is also a short introduction post on the android-developers blog.

Categories

Resources