I'm developing an app on android using Qt 5.15.0. My app creates a database and some other files using QStandardPaths::AppDataLocation. everything works fine from the code side: debug output makes me think the files are there and that the app is using them. The problem is that when I search from the PC inside the smartphone folders I cannot find the files created. So this makes me think I am not writing to the folder I'm expecting to.
This is my code:
appDataPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
QDir directory;
if (!directory.exists(appDataPath + QStringLiteral("/DATABASE")))
directory.mkpath(appDataPath + QStringLiteral("/DATABASE"));
if (!directory.exists(appDataPath + QStringLiteral("/DATA")))
directory.mkpath(appDataPath + QStringLiteral("/DATA"));
qDebug() << "Percorso: " << appDataPath << " dir exists:" << directory.exists(appDataPath + QStringLiteral("/DATABASE"));
QFile f( appDataPath + "/DATABASE/Prova.txt" );
if( f.open( QIODevice::WriteOnly ) )
{
f.write( "Ciao" );
f.close();
}
if( f.error() != QFileDevice::NoError )
qDebug() << QString("Error writing file '%1':").arg(appDataPath + "/DATABASE/Prova.txt") << f.errorString();
I also write a file that I read at startup and I can read from it so I assume the file is present and is not saved as temporary.
The output is:
D/MyApp(17849): Percorso: "/data/data/org.qtproject.example/files" dir exists: true
Now I suppose I can find my files in this directory inside the phone in this folder:
Questo PC\Samsung Galaxy J3 (2016)\Phone\Android\data\org.qtproject.example\files
But there are no directories or files inside. I bet I am missing some trivial things.
I am giving the app these permission:
android.permission.WRITE_EXTERNAL_STORAGE
android.permission.READ_EXTERNAL_STORAGE
Can someone help me?
Thanks in advance!
It seems that
QStandardPaths::AppLocalDataLocation, QStandardPaths::AppDataLocation
don't give the correct path to the app's file folder. I encountered the same issue with Qt 5.14.1. While QStandardPaths gave me
/data/user/0/com.mycompany.myapp/files
a call via a QAndroidJniObject to the java side gave me
/storage/emulated/0/Android/data/com.mycompany.myapp/files
Using the latter all directories and files were created correctly. I suggest to either try my work-around with
QtNative.activity().getExternalFilesDir(null).getAbsolutePath();
in an added java class and call it through a QAndroidJniObject or report this as a bug to Qt.
Nevertheless, there might be another solution to the problem I'm not aware of.
Related
I am concerned about two questions regarding how Qt works with Android directories.
For example, I want to download an archive via a library (QuaZip), but I need it to be downloaded along the path "/ storage / emulated / 0 / Android / data /", and then unzipped right there. But the problem is that the file is saved in the patch "/data/user/0/org.qtproject.example.NameApp". How can this be fixed? My code (it is unlikely that it works at all, but on Windows it would work for sure):
m_file.rename ("/storage/emulated/0/Android/data/cache.zip");
Can I open a file in Android via QProccess?
On your first question about Android/data r/w
On Android 11 Android/data is forbidden for app access without root, see docs1 and docs2.
For read/write to other locations:
The directory must exis or must be created (with QDir::mkpath() at once, or with QDir::mkdir() by subdirectories sequentally, because it can't create trees of directories).
QFile must be closed before rename.
App must have android.permission.READ_EXTERNAL_STORAGE and android.permission.WRITE_EXTERNAL_STORAGE permissions.
At last, approve file access permissions manually in Android apps settings after app is deployed from Qt Creator. And you also may have to launch app manually, because there were reports about different app behavior when launched from IDE and and when launched manually from device.
This code worked for me (Android 11, Qt 5.15.2):
bool result;
QDir dir("/storage/emulated/0");
qDebug() << dir;
result = dir.mkdir("cache");
qDebug() << "mkdir(\"cache\") success: " << result;
QFile m_file(dir.path() + "/cache/cache.txt");
result = m_file.open(QFile::WriteOnly);
qDebug() << "open() success: " << result;
m_file.close();
result = m_file.rename(dir.path() + "/cache/cache.zip");
qDebug() << "rename() success: " << result;
On your second question about QProcess
This code worked for me:
QProcess process;
process.start("touch", QStringList() << "/storage/emulated/0/new_text_file.txt");
result = process.waitForFinished(1000);
qDebug() << "waitForFinished() success: " << result;
qDebug() << "errorString(): " << process.errorString();
The file new_text_file.txt appeared on my device storage. If you specify the type of file you want to launch, I can answer more precise.
I have problems creating and writing files to an Android device in Qt. I have unsuccessfully tried a few different examples.
This example should write to the internal storage's Download folder:
QString path = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
qDebug() << path; // '/storage/emulated/0/Download'
qDebug() << "Exists? " << QFile::exists(path + "/401891.png"); // This is an existing image in the folder, returns true
QFile testFile(path.append("/Testing.txt"));
testFile.open(QFile::WriteOnly);
QTextStream out(&testFile);
out << "Hello";
testFile.close();
qDebug() << "Exists? " << QFile::exists(path + "/Testing.txt"); // Returns false
I get this warning:
Warning: QIODevice::write (QFile, "/storage/emulated/0/Download/Testing.txt"): device not open
What I am missing and how to fix it?
Seems like the permissions were not being applied although they were stated in Manifest.
Go to Settings -> Applications -> Application Manager
Find your application and ensure the 'Storage' permission toggle is 'On'.
You are wrong in a last line, it gives:
.../Download/Testing.txt/Testing.txt/
use
qDebug() << "Exists? " << QFile::exists(testFile.fileName()); // Returns true
I'm trying to copy a default initialized database packaged in the assets folder to an permanent location in an android device. For this I hav the following code:
QString db_file = "/my_db";
QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
QFile assets_path("assets:" + db_file);
assets_path.copy(path + db_file);
QFile::setPermissions(path + db_file, QFileDevice::ReadUser |
QFileDevice::WriteUser);
And then I would go on to open the database in this new path, the thing is I'm getting the following error after the copy.
Copy error "Cannot create /data/user/0/org.qtproject.Demo/files/my_db for output"
What am I doing wrong?. Is also this the correct/proper way to do this?
Thanks.
Well aparently is a bug in Qt 5.10 prebuilt version, rolled back to Qt 5.9 and it works. Its a shame since I needed the QML translation functions.
https://bugreports.qt.io/browse/QTBUG-64103
I am trying to make an android application using qml and C++ on QtCreator and I am not able to deploy sqlite database to android :
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
QFile file(":/patients.db") ;
if (file.exists()) {
file.copy("./patients.db") ;
QFile::setPermissions("./patients.db",QFile::WriteOwner | QFile::ReadOwner) ;
} else qDebug() << "the file does not exist" ;
db.setDatabaseName("./patients.db");
patinets.db exist under the qrc tree in QtCreator and the code work on my development host (Linux ubunntu)
But on android, the debug message "the file does not exist" gets printed out.
Am I missing something here?! What is the correct way to do this?
Thank you.
It is helpfull if you check status return codes in each step of your 'deploying'. Both, file.copy() and QFile::setPermissions() have success indicating return values.
It is highly possibile, that alreadyfile.copy("./patients.db") failes, because on android you are not allowed to create any files in same directory where your binary/apk is contained.
You need to obtain the path to internal storage allocated for your app at runtime. Link to 'Saving Files' of Android docs.
You should obtain the correct path by means of QStandartPath (e.g. AppLocalDataLocation).
You could obtain a possible target directory and place your sqlite file there like this:
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
QFile file(":/patients.db") ;
QString patientDbPath;
if (file.exists()) {
patientDbPath = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation);
if (patientDbPath.isEmpty())
{
qDebug() << "Could not obtain writable location.";
return;
}
patientDbPath.append("/patients.db");
file.copy(patientDbPath) ;
QFile::setPermissions(patientDbPath ,QFile::WriteOwner | QFile::ReadOwner) ;
} else qDebug() << "the file does not exist" ;
db.setDatabaseName(patientDbPath);
I develop Android app, which uses ciphered obb file. This obb file consists of 3 files (I created it with jobb tool). On the most devices all is ok, but on the three devices there is ERROR_COULD_NOT_MOUNT. And I don't understand how to fix it. I want to distribute app without any errors :) Please, help me to do it! :)
That I have tryed:
1) Unmount and mount obb file;
2) Create obb file from the folder without "read-only" attribute.
But all of it won't work.
Steps to reproduce the problem (including sample code if appropriate).
1) Create folder, put into it 3 different files.
2) Create obb file from the folder from step 1.
3) Try to mount this file from the app (sample code below).
final File mainFile = new File(Environment.getExternalStorageDirectory() + "/Android/obb/" + packageName + "/"
+ "main." + versionCode + "." + packageName + ".obb");
OnObbStateChangeListener listener = new OnObbStateChangeListener() {
#Override
public void onObbStateChange(String path, int state) {
super.onObbStateChange(path, state);
if (state == OnObbStateChangeListener.MOUNTED) {
doNextSteps();
} else if (state == OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT) {
Toast.makeText(getApplicationContext(), "ERROR_COULD_NOT_MOUNT", Toast.LENGTH_LONG).show();
}
}
};
if (!storageManager.isObbMounted(mainFile.getAbsolutePath())) {
storageManager.unmountObb(mainFile.getAbsolutePath(), true, listener);
storageManager.mountObb(mainFile.getAbsolutePath(), "password_string", listener);
} else {
doNextSteps();
}
What happened.
I have "ERROR_COULD_NOT_MOUNT" error on the following devices:
1) HTC PJ401 One S;
2) Samsung GT-I9505 Galaxy S IV;
3) Samsung SM-N9005 Galaxy Note 3.
But on Samsung SM-N900T Galaxy Note 3 and Samsung GT-I9500 Galaxy S IV all is ok.
So, after all my investigations I can say. All I can to do is workaround. And I have created one file from the these 3 files by
copy /B file1+file2+file3 result_file.obb
And then I use this file as non-ciphered obb. After all this manipulations all works well.
I find the reason for some obb file could not be mounted. There a random solt when make obb file, an hashkey generated by PBKDF2WithHmacSHA1. the hashkey from bytes convert to hex string has a bug. I submit a patch to project : platform/framwork/base.
https://android-review.googlesource.com/#/c/230280/
I modified the jobb tool. Add check the hashkey to skip the wrong key generated. Before the Android merge the patch, you can use this patch. This patch also fix the dump files bug.
https://android-review.googlesource.com/#/c/231431/