I want to save some user credentials in my qt application that runs on Android.
I use QSettings for this like so:
QString appPath = QCoreApplication::applicationDirPath()+QLatin1Char('/');
set = new QSettings(appPath+"test",
QSettings::NativeFormat);
set->setValue ( "user/username", "NameOfTheUser" );
set->setValue ( "user/password", "UserPassword" );
set->sync();
I restart the app and inside an initialize() method I have:
QString username(set->value("user/username",
( QVariant ) tr ( "defaultUser" ) ).toString());
QString password(set->value("user/password",
( QVariant ) tr ( "defaultPass" ) ).toString());
The username and password vars are not read from the QSettings.
The same code is working on Windows.
Thank you
I also ran into similar problem and found out that 2 things should be done:
path to settings file should be specified
QSettings::sync() should be explicitly called after every settings change.
So on Windows I had this working:
QSettings settings("settings.ini", QSettings::IniFormat);
settings.setValue(GRID_ENABLED, enabled);
On Android I have to use the following code:
QSettings settings("/sdcard/settings.ini", QSettings::NativeFormat); //can be IniFormat, no difference
settings.setValue(GRID_ENABLED, enabled);
settings.sync();
Probably using "sdcard" is not good enough and you should use other directory.
You can try to specify the location of the setting file to a writable location which exists even if the application is removed :
#include <QSettings>
#include <QStandardPaths>
QString path ;
QString filename;
path = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) ;
filename = “config.ini” ;
QSettings settings(path + “/”+ filename,QSettings::IniFormat) ;
Here QStandardPaths::GenericDataLocation returns a directory location where persistent data shared across applications can be stored and it is never empty.
Also you can set the application and organization name in the main of your application once :
qApp->setOrganizationName("Company");
qApp->setApplicationName("App");
As noted, a re-deploy of the application from Qt Creator wipes the settings.
This is still true in Qt Creator 3.3.2 with the following caveat. When you deploy the app on Android and look at the Application Output window, there is a tool bar with a "stop" button (red square) and a "Re-run this run configuration" button (green triangle).
The initial deploy from Qt starts the app. The QSettings object is cleared or empty. Any changes to the QSettings object will be saved in the object.
If you stop the app with the red button, then immediately restart the app with the green Re-run button, the app will restart and all changes to the QSettings object in the previous run will still be there.
I assume this emulates the start, exit, restart of the app on a device.
Hi I've found the solution, tested on 3 different Android device. You can set the following path for your settings.
mPath = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
QString filename = "se.ini";
mPath = mPath + "/" + filename;
By following code you save your info under above location.
void ProfileManager::saveToRegistery()
{
QSettings settings(mPath , QSettings::NativeFormat);
settings.setValue("SE/Mail" , mMail);
settings.setValue("SE/Pass" , mPass);
settings.sync();
}
If you have any trouble with saving that place, you ask user permission for accessing any file with:
bool QAndroidPermissions::requestPermissions()
{
QtAndroid::PermissionResult r =
QtAndroid::checkPermission("android.permission.WRITE_EXTERNAL_STORAGE");
if(r == QtAndroid::PermissionResult::Denied) {
QtAndroid::requestPermissionsSync( QStringList() <<
"android.permission.WRITE_EXTERNAL_STORAGE" );
r =QtAndroid::checkPermission("android.permission.WRITE_EXTERNAL_STORAGE");
if(r == QtAndroid::PermissionResult::Denied) {
return false;
}
}
return true;
}
Hope this helps,
I had a problem similar to the above, and it turned out that everything worked just fine. That is it did work with a simple QSettings object without any arguments.
HOWEVER, every time I re-deployed the application from Qt Creator, the settings file was destroyed, leading me to the conclusion that QSettings did not work.
The problem should, according to Bogdan himself, have been fixed in Qt Creator 3.2
The only problem with your code is that for Android you have to use QStandartPaths to get path and not the QCoreApplication::applicationDirPath().
QCoreApplication::applicationDirPath() will give you the windows application path to your Android Application, which won't work on Android.
Related
I am programming an android game with Godot 3.2.2. Of course I want to save the game data, therefore I created a new scene called SaveGame.tscn with SaveGame.gd. I exported the project to my windows pc and everything worked fine, all datas were saved. After that I also exported it to my android phone, but this time no data was saved. Instead it showed me that it could not find the save path. Maybe anybody of you can help me with this problem so that data is also saved on android.
SaveGame.gd:
extends Control
const FILE_NAME = "user://save_game_data_one.json"
var save_game_data
func _ready() -> void:
Signals.connect("load_data", self, "load")
Signals.connect("game_quit", self, "save")
save_game_data = {
"highscore": Globals.highscore,
"coins": Globals.coins,
"music_volume": Globals.music_volume,
"sound_volume": Globals.sound_volume,
"button_gameplay": Globals.button_gameplay,
"button_gameplay_side": Globals.button_gameplay_side,
}
func save():
update_data()
var file = File.new()
file.open(FILE_NAME, File.WRITE)
file.store_string(to_json(save_game_data))
file.close()
func load():
var file = File.new()
if file.file_exists(FILE_NAME):
file.open(FILE_NAME, File.READ)
var data = parse_json(file.get_as_text())
file.close()
if typeof(data) == TYPE_DICTIONARY:
save_game_data = data
else:
printerr("Corrupted data")
else:
self.show() <---- Here it shows me the hidden scene (white rectangle), this means it could not find FILE_NAME
export_data()
func export_data():
Globals.highscore = save_game_data["highscore"]
Globals.coins = save_game_data["coins"]
Globals.music_volume = save_game_data["music_volume"]
Globals.sound_volume = save_game_data["sound_volume"]
Globals.button_gameplay = save_game_data["button_gameplay"]
Globals.button_gameplay_side = save_game_data["button_gameplay_side"]
func update_data():
if Globals.score_round >= Globals.highscore:
save_game_data["highscore"] = Globals.score_round
save_game_data["coins"] += Globals.coins_round
I also would like to know the answer. I am trying to figure a way to have "persistent time" in my android game where "time passes" in game even when closed. Best solution I figure so far is getting the unix time on the games first start and check against it when reopening the app. My problem is finding a way to save that original unix time to call upon for checks.
I am currently developing an app for Android and IOS using Qt Quick Controls 2 which will have login possibility. The account credentials will be stored into an external SQL database. I have figured out how I can interact with the database, but I am not able to figure out how I can use sessions in Qt for Android and IOS such that the user do not need to type the login credentials each time.
One possible way could be to store the credentials to a file locally on the phone, and then access the file each time the app is started. However, I am not sure if this is the best and safest way.
Thanks.
QSettings class is used for state management. QSettings information is often stored in the system registry on Windows, and in property list files on macOS and iOS. Here is raw example code that uses QSettings to store/retrieve session parameters .. you can extend to include needed session login management scenarios:
main.cpp
int main( int argc, char ** argv ) {
QApplication app( argc, argv );
app.setOrganizationName("myOrg");
app.setOrganizationDomain("myOrg.net");
app.setApplicationName("qsettings-test");
MyMainWindow mw;
mw.show();
return app.exec();
}
Then in your source .cpp
void MyMainWindow::readSettings() {
QSettings settings;
QString user = settings.value("UserName");
int lifTime = settings.value("sessionLifeTime").toInt();;
QByteArray state = settings.value("state", QByteArray())
.toByteArray();
restoreState(state);
}
// ...... lots of your code
void MyMainWindow::closeEvent(QCloseEvent* event) {
if (maybeSave()) {
writeSettings();
event->accept();
} else {
event->ignore();
}
}
void MyMainWindow::writeSettings() {
/* Save postion/size of main window */
QSettings settings;
settings.setValue("sessionLifeTime", 7);
settings.setValue("UserName", user);
settings.setValue("state", saveState());
}
You can also access INI and PLIST configuration files with QSettings class, For Android this can be considered where your INI file must be copied during build (store in res folder and include it in .pro ).
I'm studying the android kernel as a beginner. I can read the messages thrown from the macro ERROR() inside the function main() at system/core/init/init.c using dmesg command through adb. I observed that after calling the function open_devnull_stdio() inside main(), dmesg no longer displays the messages thrown by ERROR().
To find the reason, I started digging into the declaration of open_devnull_stdio() inside system/core/init/util.c and I found this line I can't understand
static const char *name = "/dev/__null__";
Actually there was no file named __null__ inside /dev/ in the device, but there was a file named null and I was able to grab it using adb pull and it was a 0 byte (empty) file.
So why is a file name wrapped with double underscore (__) ?
Here is the link for the util.c
There is no special purpose of using double underscore before the start, after the end or both in C. From the point of view of C the file name is just a string, the operating system is free to interpret in whatever way it chooses. From the point of view of Linux, the same applies. Underscores in file names are just characters. They are not treated differently from the letters b and t.
If I guessed right and I'm reading the same file as you (it might be a good idea to link to the source code you're reading) then it should be pretty obvious what the code does on the lines after the one you mentioned. The next lines are:
if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {
fd = open(name, O_RDWR);
unlink(name);
Which creates the null device which is then opened and immediately deleted again.
I suspect this is done so that programs can run without access to the root filesystem and still be able to open the equivalent of /dev/null.
I don't know the answer but I have an idea:
The following page shows an "strace" output where /dev/__null__ is used:
https://gist.github.com/tetsu-koba/1522515
Under Linux device files have a 33-bit (?) number which identifies the device. (At least under old Linux versions) you could delete some file in /dev and you could restore it or even create it in another directory (!) when you know the 33-bit number! (So you can delete the device /dev/sda2 and create the device (not file!) /home/myuser/sda2 instead.)
The trace in the link above shows the following three lines:
mknod("/dev/__null__", S_IFCHR|0600, makedev(1, 3)) = 0
open("/dev/__null__", O_RDWR|O_LARGEFILE) = 3
unlink("/dev/__null__") = 0
These lines will create the device file /dev/__null__ (with the 33-bit number identifying /dev/null). Then it opens that file and then it removes the file again.
Maybe this is done because the tool shall be able to run both on Linux installations where the device file "/dev/null" is present (in this case the file should not be overwritten) and on installations where that file is missing (in this case a replacement file must be created using the known 33-bit number).
As other people have pointed out this just tells it's the "null device", not a regular file called "null". null is supposed to act like an information sink, not like a normal file where you dump your data to. Hope this helps.
My code used to work, it does not work anymore, I tried troubleshooting and can't figure out why.
I have this piece of code in my PHP:
$android_id_01 = $_GET['pmysql_room_id'];
$android_id_02 = "";
$f = fopen("00_android_id_01.txt", "w");
fwrite($f, print_r($android_id_01, true));
fclose($f);
$f = fopen("00_android_id_02.txt", "w");
fwrite($f, print_r($android_id_02, true));
fclose($f);
For troubleshooting I created two android IDs ($android_id_01 and $android_id_02) which are both empty (The first one is From Android and the second one I created directly from PHP).
Now when I launch my Android device, the PHP file is executed from server side and both the text files are created empty and identical. Now my code only works when I use $android_id_02 and not $android_id_01 from the code below:
if ($android_id == '')
{
//my code
}
(Yes when I use either one of the $android_id_01 OR $android_id_02 I rename it to $android_id and comment out the other one)
My question is, although this was working yesterday, why does it work with $android_id_02 = ""; and not $android_id_01 = $_GET['pmysql_room_id']; even though they are both empty????
I don't know what changed from yesterday to today.
Ok after a bit of troubleshooting I found a solution, strange though.
On the server side "display_errors" under PHP settings must be turned off. Somehow having this on interferes with the json_encode sent back to android client. (even though my code is not generating any errors)
For some reason, when I restart my PhoneGap app - it looses the localStorage vales that were stored before! I'm saving them in the normal way:
localStorage.setItem("foo","value");
This stores it just fine. However, when you restart the app (or leave the device off for a random amount of time), it seems to randomly loose the data. I've found a heck of a lot of posts about this - but no definative answer on how to get it to be persistent in a PhoneGap Build WebView app,
Any suggestions are much welcomed!
This seems to be quite a common problem with WebView apps:
Android 2.3.6 + Phonegap + localStorage
Android - Making Webview DomStorage persistant after app closed
I can't find a solution that works with PhoneGap Build apps though
An actual example I'm using, is:
var current_id = parseInt(currentId) + 1;
localStorage.setItem("entry_"+current_id,save_string);
localStorage.setItem("entryId",current_id);
..and then to extract it (not that this is important, as the problem is with the data going missing, and not with accessing it)
for (var i = 0; i < localStorage.length; i++){
if (localStorage.key(i).match("entry_")) {
outputString += "\n" + localStorage.getItem(localStorage.key(i));
}
}
I'm wondering if maybe upgrading from PhoneGap Build cli-5.2.0 to cli-6.0.0 may help. I will do this, and give it a whirl.
I guess another option, would be to use a SQL database to locally store the device (its just a bit trickier to setup, and means re-writing my code)
UPDATE: Not the ideal solution - but I have now moved the app over to use WebSQL for the app. It was a bit tricky to get the hang of (never used it before) - but seems to do the job, and shouldn't loose the data :)
EDIT
i tried it like this and it worked:
var current_id = parseInt(currentId) + 1;
localStorage.setItem("entry_"+current_id,save_string);
localStorage.setItem("entryId",current_id);
/*
//this is for checking, what is stored in localStorage
console.log("length: " + localStorage.length);
for(var i = 0; i < localStorage.length; i++) {
console.log(localStorage.key(i));
}
*/
var myEntryIdFromStorage = localStorage.getItem("entryId");
var myItem = localStorage.getItem("entry_" + myEntryIdFromStorage);
Old answer for clarification
How do you get your localstorage?
normally you should store items like you did:
var permanentStorage = window.localstorage;
permanentStorage.setItem("foo", "bar");
and get them back by initializing the permanentStorage the same way and:
//assuming you have permanentStorage in the same script file
//or else you have to initialize it again:
//var permanentStorage = window.localstorage;
var myItem = permanentStorage.getItem("foo");
console.log("myItem: " + myItem);
The method store item uses two parameters: the identifier and the data itself. Please check, that the identifier with which you store your data is the same as the one, with which you get it back.
Do you get any errors? Is the return (stored in my example in myItem) null or undefined or just an empty string? Does this fail in the browser or on the device?
You could clarify your question by providing more code or error messages!