I am trying to list the files of a directory on Android through flutter, I am using file_picker to access it but it lists nothing even for a directory full of files.
I have tried opening a directory with files in it and printing it out:
void _pickDirectory() async {
final path = await FilePicker.platform.getDirectoryPath();
if (path == null) {
print('Canceled');
} else {
final directory = Directory(path);
final files = directory.listSync();
print(files);
}
}
but it just prints out an empty array.
I've added
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
to my android/app/src/main/AndroidManifest.xml and reinstalled the app but it still shows no files.
Any help is appreciated, thank you
Related
I have an application that generates some PDFs and CSV files and at the moment I am saving these files into the files folder in the application directory of the Android phone. Then the user needs to manually move the files to another folder (for example Documents or Downloads).
I would like to save the file to the Documents or Download folder (one of the two, is the same) automatically at runtime, after the files are generated by the app. The target devices has Android 11 so I have any kind of problems with managing the external storage.
Android Manifest:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.app.example">
<!-- PERMISSIONS -->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
...
...
Permissions requested at startup:
requestPermissions() async {
try {
await [
Permission.storage,
Permission.manageExternalStorage,
].request();
} catch (e) {
LoggerService().error("PERMISSIONS REQUEST ERROR", e.toString());
}
}
Method that saves the files:
Future<String> export(Uint8List pdf, {String? fileName}) async {
Directory dir = Directory(EnvironmentService.documentsPath);
String path = "${dir.path}/${fileName ?? "${uuid.v4()}.pdf"}";
File file = File(path);
await file.writeAsBytes(pdf);
return path;
}
Path where I am trying to save the files:
static const String documentsPath = "/storage/emulated/0/Documents";
When saving the file I always get OS Error: Operation not permitted, errno = 1) error, even if I have granted the MANAGE_EXTERNAL_STORAGE permission. The application will not be published on the Play Store, and needs to be working only on Android.
Any idea of what's happening?
Am using this code to try to read a particular directory an android devices.. this is the code in a released application. it works fine on all devices except SamSung devices..
fetchFromOriginalDirectory() async{
var result = await PhotoManager.requestPermission();//getting permission to check files
if (result) {
Directory dir = Directory('/storage/emulated/0/');
List<FileSystemEntity> _files;
_files = dir.listSync(recursive: true, followLinks: false).reversed.toList();//getting all files(stickers) in this directory
for(FileSystemEntity entity in _files) {//for each file gotten do this
String path = entity.path;
I first thought the problem was this line "/storage/emulated/0/" but after using multiple plugins to check for the right path. i found out the paths are the same. But for some reason the app cant read the files on samsung files. even after all permissions have been granted
Hey guys i fixed it by doing this
<application
android:requestLegacyExternalStorage="true" //adding this line
>
In the android manifest file.
There are various articles on how to import data on the internal database from a csv, from another database etc .. but I haven't found anyone explaining how to export the flutter database.
The goal is to create a backup for each cell phone.
(So I need to understand where it is located for create a backup)
I assume you are using sqflite plugin for SQLite operations and path_provider for storage. The path of the database can be found using
String path = await getDatabasesPath(); // which is data/data/<package_name>/databases
Additionally, sqflite plugin doesn't provide any way to import/export database, here is an open issue, if you really want to do it, you will have to do it natively using MethodChannel, here is the solution for Android and AFAIK there is no way to do it in iOS.
if you use Sqlite to create database:
Step 1: When you create database, you set a directory for it, you can use path_provider like this:
var dir = await getApplicationDocumentsDirectory();
_dbPath = dir.path + '/$dbName';
so now you know what directory and path it is.
Step 2: Then use flutter_archive plugin to zip the file (This will zip the file in a directory, which is your db);
Step 3: use flutter_email_sender to send it by email, like this:
final email = Email(
body: 'content',
subject: 'content',
recipients: ['email'],
cc: ['email'],
attachmentPaths: [exportPath],
isHTML: false,
);
await FlutterEmailSender.send(email);
Your need to provide exportPath, which is the zip file path you set up.
It worked for us, hope this will help other people!
Usually on Android, databases are stored at
/data/data/your.app.signature.here/databases/.
But I don't know if that's different on flutter apps.
Hope that helps.
dependencies:
sqflite: ^2.0.0+3
path_provider: ^2.0.11
permission_handler: ^10.0.0
Export ( back up)
To export SQFLite database , I came across some errors , some of the error are
FileSystemException: Cannot open file, path
error: permission denied, errno = 13
etc........
I want to export my Database into Download folder that is ,
this is my Database path /data/user/0/com.example.reminder_app/databases/notes.db , it's a application directory path so my aim is to export notes.db file into this path
/storage/emulated/0/Download/
Expanding dBToCopy functions , this function will give path of Database
Future<File> dBToCopy() async {
final db = await instance.database;
final dbPath = await getDatabasesPath();
var afile = File(dbPath);
return afile;
}
full code bellow
dbExportToDownloadFolder() async {
File result = await NotesDatabase.instance.dBToCopy();
print("lllllllllllllllllll ${result.absolute.path}");
Directory documentsDirectory =
Directory("storage/emulated/0/Download/");
String newPath = join(documentsDirectory.absolute.path + 'abcde.db');
File b =
File("/data/user/0/com.example.reminder_app/databases/notes.db");
if ( await Permission.storage.request().isGranted &&
await Permission.accessMediaLocation.request().isGranted &&
await Permission.manageExternalStorage.request().isGranted )
{
File a = await b.copy(newPath);
} else {
print("No Permission Granted");
}
}
Note
File result = await NotesDatabase.instance.dBToCopy();
print("lllllllllllllllllll ${result.absolute.path}");
OutPut print
lllllllllllllllllll /data/user/0/com.example.reminder_app/databases
this result file not contain the notes.db file , only contain this path
/data/user/0/com.example.reminder_app/databases
To get the DatabaseFile
File b = File("/data/user/0/com.example.reminder_app/databases/notes.db");
or
File b = File("${result.path}"+"/notes.db");
so using the file b we can copy the file to Download folder file that is abcde.db
To do that create a file in Download , that is abcde.db
Directory documentsDirectory = Directory("storage/emulated/0/Download/");
String newPath = join(documentsDirectory.absolute.path + 'abcde.db');
and using the copy method , to copy one file to another file
File a = await b.copy(newPath);
Note
If you are getting permission denied errors and OS errors please add all permission in manifest , and using permission_handler allow all permissions
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION"/>
after the copying , A new file created in Download folder that is abcde.db
While running the app on the emulator, system reports an error while cpp code trying to create a directory in folder "/home/cocadas/Workspace/android-project/JNIAppSample".
Java will call a JNI cpp function to create the directory.
The cpp source code is as following:
static int createEventDir(void)
{
int result;
int stringLen;
time_t currentTime = time(0);
struct tm * now = localtime(¤tTime);
stringLen = sprintf(thisEventParms.eventDirectory,
ADAN_EVENT_BASE_DIR, now->tm_mon + 1,
now->tm_mday, now->tm_hour,
now->tm_min, now->tm_sec);
if (stringLen > 0)
{
result = mkdir(thisEventParms.eventDirectory, 0700);
}
else
{
// TBD: Error, unable to make event directory
result = -1;
}
return(result);
}
Android Studio debug reports result = -1 after executing result = mkdir(thisEventParms.eventDirectory, 0700); Also, debug reports thisEventParms.eventDirectory = "/home/cocadas/Workspace/android-project/JNIAppSample", which is expected.
After some research, I add one permission in manifest like the following:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
But it still gives result = -1
Any comment or suggestion?
I guess that the android emulator has the same structure as any android device.
So, /home/cocadas...etc doesn't exist in your emulator.
If the response is -1, usually seems that your path is not writable.
In conclusion, writable path could be "/sdcard/your_directory." and of course you will need to add the permission you written above.
If you want to get an writable path but inside of your apk use this code.
String config_path=m_context.getApplicationContext().getFilesDir().toString();
Cheers.
I'm trying to build an Android app which makes use of the NativeActivity facility of the NDK.
I'm having the following structure:
a bunch of native shared libraries installed in /system/vendor/<company>; I'm working
with a custom built Android image so there's no problem having the libraries there with
proper permissions and everything
a couple of applications using the NativeActivity that depend in turn on the libraries
mentioned above
The libraries installed in the /system/vendor and my applications use a couple of
configuration files. There's no problem reading them using the standard C API
fopen/fclose. But those libraries and my application also need to store some files
as the result of their operation, like configuration, some run-time parameters, calibration
data, log files etc. With the storing of the files there is a slight issue as I'm not allowed to write into /system/vendor/... (as the file system under "/system/..." is mounted read-only and I do not want to hack on that).
So what would be the best way to create and store those files and where would be the
best "conforming with Android" storage area ?
I've been reading a couple of threads in the android-ndk Google group and here on SO that mention either the internal application private storage or the external SD card, but as I do not have extended experience with Android I'm not sure what would be the proper approach. If the approach involves some specific Android API a small code example in C++ would be very helpful; I've seen a couple of examples involving Java and JNI (e.g. in this SO question) but I would like to stay away from that right now.
Also there seems to be a problem with using from C++ the native activity's
internalDataPath/externalDataPath pair (a bug that makes them be always NULL).
For relatively small files(application config files, parameter files, log files etc.)
is best to use the internal application private storage, that is /data/data/<package>/files.
The external storage if it exists at all (being it SD card or not) should be used for large files that do not need frequent access or updates.
For the external data storage the native application has to "request" the correct permissions in the application's AndroidManifest.xml:
<manifest>
...
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE">
</uses-permission>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE">
</uses-permission>
</manifest>
For the internal application private storage fopen/fclose(or C++ stream equivalents if available) API could be used. Following example illustrates using the Android NDK AssetManager to retrieve and read a configuration file. The file must be placed into the assets directory inside the native application’s project folder so that the NDK build could pack them inside the APK. The internalDataPath/externalDataPath bug I was mentioning in the question was fixed for the NDK r8 version.
...
void android_main(struct android_app* state)
{
// Make sure glue isn't stripped
app_dummy();
ANativeActivity* nativeActivity = state->activity;
const char* internalPath = nativeActivity->internalDataPath;
std::string dataPath(internalPath);
// internalDataPath points directly to the files/ directory
std::string configFile = dataPath + "/app_config.xml";
// sometimes if this is the first time we run the app
// then we need to create the internal storage "files" directory
struct stat sb;
int32_t res = stat(dataPath.c_str(), &sb);
if (0 == res && sb.st_mode & S_IFDIR)
{
LOGD("'files/' dir already in app's internal data storage.");
}
else if (ENOENT == errno)
{
res = mkdir(dataPath.c_str(), 0770);
}
if (0 == res)
{
// test to see if the config file is already present
res = stat(configFile.c_str(), &sb);
if (0 == res && sb.st_mode & S_IFREG)
{
LOGI("Application config file already present");
}
else
{
LOGI("Application config file does not exist. Creating it ...");
// read our application config file from the assets inside the apk
// save the config file contents in the application's internal storage
LOGD("Reading config file using the asset manager.\n");
AAssetManager* assetManager = nativeActivity->assetManager;
AAsset* configFileAsset = AAssetManager_open(assetManager, "app_config.xml", AASSET_MODE_BUFFER);
const void* configData = AAsset_getBuffer(configFileAsset);
const off_t configLen = AAsset_getLength(configFileAsset);
FILE* appConfigFile = std::fopen(configFile.c_str(), "w+");
if (NULL == appConfigFile)
{
LOGE("Could not create app configuration file.\n");
}
else
{
LOGI("App config file created successfully. Writing config data ...\n");
res = std::fwrite(configData, sizeof(char), configLen, appConfigFile);
if (configLen != res)
{
LOGE("Error generating app configuration file.\n");
}
}
std::fclose(appConfigFile);
AAsset_close(configFileAsset);
}
}
}