How can I save a file locally on an Android device,
using Delphi (XE5, Firemonkey)?
Something as simple as
Memo.Lines.SaveToFile('test.txt')
does not seem to work.
It results in the following error message:
"Cannot create file "/test.txt". Not a directory."
According to the document Creating an Android App, get the documents path like this:
System.IOUtils.TPath.GetDocumentsPath + System.SysUtils.PathDelim + 'myfile';
Instead of using System.SysUtils.PathDelim, you may use
System.IOUtils.TPath.Combine(System.IOUtils.tpath.getdocumentspath,'test.txt');
Combine chooses between the windows \ and the Linux /
System.IOUtils must be used in this line instead of setup in the uses clause because there are probably more Tpath initials.
GetHomePath, return a string with the path.
-> 'data/data//files'
You can to use:
Memo.Lines.SaveToFile(format('%s/test.txt', [GetHomePath]));
Or this form
Memo.Lines.SaveToFile(GetHomePath + '/test.txt');
on my device (and presumably all android devices?) GetHomePath incorrectly gives me /storage/emulated/0/... whereas I need /storage/sdcard0/... to get to the storage visible in Windows Explorer via USB.
so the full path to my files may be
'/storage/sdcard0/Android/data/com.embarcadero.(my app name)/files/'
Presumably if you have a plug in SD card this might be sdcard1 or whatever.
You can list the contents of your device storage folder with code like this
P := '/storage/';
if (FindFirst(P + '*', faAnyFile, Sr) = 0) then
repeat
Memo1.Lines.Add(Sr.Name);
until (FindNext(Sr) <> 0);
FindClose(Sr);
On my device this gives me:
sdcard0
usb
emulated
then change S when you want to explore subfolders
Note that the files folder gets emptied each time you recompile and deploy.
Related
Is there any code to check whether or not the file's path is the filesytems ("/storage" or "/storage/emulated/0" or "/") in Android 7 ? Because when I call below code, it returns null in case the path of "folder" relates to filesystems in Android 7. I want to check to show some message like "This is the File System of Android" before calling below code.
File[] childFiles = folder.listFiles();
Use folder.exists(). Or folder.canRead().
But in Android 7 its impossible to list the "/" directory. And some other directories as you have seen.
Check childFiles for null and return if so.
Bad times ;-).
We are trying to included some bundled images with our app which integrates with URLImages but unzipping them into the storage folder (with url paths that match those expected).
It works fine on ios and in the simulator, but is failing to find or have access to the correct path on Android.
String storagePath = getStoragePath(url + suffix);
OutputStream os = Storage.getInstance().createOutputStream(storagePath);
...
getStoragePath(String string){
for (String s : Arrays.asList("/", "\\", "%", "?", "*", ":", "=")) {
string = StringUtil.replaceAll(string, s, "_");
}
return string;
}
On android the path seems to be wrong from this.
Is there any way to manually get the correct path and then use FileSystemStorage to open the output stream?
I'd rather not hard code any paths since they do occasionally change, and can vary from system to system.
Any other options I'm not seeing here?
You are coding to an implementation detail which is specifically hidden and might change in the future. FileSystemStorage & Storage are logically separate and shouldn't be mixed.
If you want access from FileSystemStorage you should always only use that API and avoid Storage.
This is by design, in some OS's storage is implemented in a very unique way that doesn't map to a filesystem file at all...
Using Titanium on Android 4+ I want to access a jpeg file which has been taken with the camera. I need to achieve 2 objectives, namely, return the EXIF data and transfer the bytes to an API endpoint. My problem is I'm unable to access the file...
I'm using a 3rd party module to handle the file selection (Multi Image Picker) which returns a list of file locations, using the File Manager app on the emulator (GenyMotion) I can confirm the location on disk is correct. However, the following always returns false...
var file = Ti.Filesystem.getFile('/mnt/sdcard/DCIM/Camera/IMG_20140901_083735.jpg');
Ti.API.info('Do we have a file? ' (file.exists()? 'YES' : 'NO'));
The output for the above would be... Do we have a file? NO
Further reading shows Titanium has 5 predefined folder locations which can be passed into the getFile() method and one possible reason for the above code not working would be it is defaulting to the 'Resouces' folder location? That said all but one folder location is app specific, the exception being externalStorageLocation. Now my understanding of an Android device is that any image taken with the camera will be stored on the internal storage system unless an SD card is present. This is true in my case as the following lists 0 files...
var extDir = Ti.Filesystem.getExternalStorageDirectory();
var dir = Ti.Filesystem.getFile(extDir);
var dir_files = dir.getDirectoryListing();
Ti.API.info('External files... ' + dir_files.length);
The output for the above would be... External files... 0
So am I right in thinking Appcelerator have simply not included the ability to access local storage (outside of any app specific folders) within their API? Or am I missing something and there is in fact another way?
Thanks to #Bharal I was able to find a solution...
By using the Ti.Media.openPhotoGallery() method I was able to identify the correct native path for the image by inspecting the event object returned from the success callback.
The path was missing 'file://' at the beginning, I couldn't be 100% sure but I suspect this forces the getFile() method to use an absolute path and not a relative path from within the Resources folder.
To confirm, the following will return a file object...
var file = Ti.Filesystem.getFile('file://[path]');
Where [path] is the folder location as reported within the File Manager app on the device, for example '/mnt/sdcard/DCIM/Camera/IMG_20140901_083735.jpg'
Yah mon, i dunno.
Here is wat i used when i was doin pictures on my Ti app, but then i got rid of that section because i realised i didn't need to be doin pictures. Pictures mon, dey ain' what you want sometimes, yo?
Ti.Media.openPhotoGallery({ //dissall jus' open up a piccha selectin' ting. ez.
success:function(event){
var image = event.media;
if (event.mediaType==Ti.Media.MEDIA_TYPE_PHOTO){
//so image.nativePath is the path to the image.
// profileImg be jus' some Ti.UI.createImageView ting yo be puttin in yo' page.
//meyybe yo be wantin' alert(image.nativePath); here too, dat be helpin?
profileImg.image = image.nativePath;
}
},
cancel:function(){
//we cancelled out, why we doin' that?
}
});
Now that isn't going to really be helpin' you, but yo can use that to see wat the native path yo piccha be usin' be, and then be seein' if maybe what yo be puttin' in yo code be sam ting.
Jus' wrap the above as an addEventListener("click", function(){ ... } ); on sam ting in yo page, and jus' add sam element to put th' piccha in if yo be wantin' to see the piccha but i be tellin' you picchas mon, sometimes dey ain' worth time.
But meyybe yo wantin' use not an emulator for dis ting, dey can be actin' weird yo should be usin some small phone maybe? Dat way you can be findin' if yo got dem memory leeks and meyybe some memory sprouts, an memory onions too.
I'm goig to be mad with a strange issue. If i create a folder inside my code as
directory_path = Environment.getExternalStorageDirectory()
+ "/" + context.getResources().getString(R.string.app_name);
directory = new File(directory_path);
if (!directory.exists()) {
directory.mkdirs();
}
a new folder is created inside /sdcard/ . If i try to print on logcat directory_path variable, the path is different: /storage/emulated/0/
and if i go to that path, i found another folder with the same name of the one created on /sdcard/ . This is a problem for me because when i try to write some data into that folder, everithing goes in the one on /storage/emulated/0 , and the other one (that is the folder i want use) remain empty.
Why?
Have you tried reading back the data? /storage/emulated/0/ is the new path introduced in JB to support multiple users on tablet. But as long as you access external files using Environment.getExternalStorageDirectory() it doesn't really matter where they really reside.
Here's some additional info: https://android.stackexchange.com/questions/35541/why-did-sdcard-turn-into-sdcard-0-with-4-2
/storage/emulated/0/: to my knowledge, this refers to the "emulated
MMC" ("owner part"). Usually this is the internal one. The "0" stands
for the user here, "0" is the first user aka device-owner. If you
create additional users, this number will increment for each.
/storage/emulated/legacy/ as before, but pointing to the part of the
currently working user (for the owner, this would be a symlink to
/storage/emulated/0/). So this path should bring every user to his
"part".
/sdcard/: According to a comment by Shywim, this is a symlink to...
/mnt/sdcard (Android < 4.0)
/storage/sdcard0 (Android 4.0+)
For more detail you can visit stackexchange
How can I save a file locally on an Android device,
using Delphi (XE5, Firemonkey)?
Something as simple as
Memo.Lines.SaveToFile('test.txt')
does not seem to work.
It results in the following error message:
"Cannot create file "/test.txt". Not a directory."
According to the document Creating an Android App, get the documents path like this:
System.IOUtils.TPath.GetDocumentsPath + System.SysUtils.PathDelim + 'myfile';
Instead of using System.SysUtils.PathDelim, you may use
System.IOUtils.TPath.Combine(System.IOUtils.tpath.getdocumentspath,'test.txt');
Combine chooses between the windows \ and the Linux /
System.IOUtils must be used in this line instead of setup in the uses clause because there are probably more Tpath initials.
GetHomePath, return a string with the path.
-> 'data/data//files'
You can to use:
Memo.Lines.SaveToFile(format('%s/test.txt', [GetHomePath]));
Or this form
Memo.Lines.SaveToFile(GetHomePath + '/test.txt');
on my device (and presumably all android devices?) GetHomePath incorrectly gives me /storage/emulated/0/... whereas I need /storage/sdcard0/... to get to the storage visible in Windows Explorer via USB.
so the full path to my files may be
'/storage/sdcard0/Android/data/com.embarcadero.(my app name)/files/'
Presumably if you have a plug in SD card this might be sdcard1 or whatever.
You can list the contents of your device storage folder with code like this
P := '/storage/';
if (FindFirst(P + '*', faAnyFile, Sr) = 0) then
repeat
Memo1.Lines.Add(Sr.Name);
until (FindNext(Sr) <> 0);
FindClose(Sr);
On my device this gives me:
sdcard0
usb
emulated
then change S when you want to explore subfolders
Note that the files folder gets emptied each time you recompile and deploy.