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!
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 have a simple array that takes rows from a database and assigns a distance column as a key.
let output = {};
for (let dataRow of sqllite.rows) {
output[dataRow.distance] = dataRow;
}
In testing in Chrome browser on PC, it takes less than a second to complete, but on an Android device, it just hangs.
What's the best way to handle this?
Thanks
Mark
After a stab in the dark, this fixed it. Changing from Typescript to Javascript.
let output = {};
for (var outputIndex in sqllite.rows) {
output[sqllite.rows[outputIndex].distance] = sqllite.rows.rows[outputIndex];
}
No idea why TS is slower
I have a form in which user can add information and add images. The images are base64 encoded so everything is stored in a json object. This object is sent to the server (with $resource) when the user submits it.
If a user adds for example 3 Images with about 2MB per Image, has a shitty connection like Edge, and wants to upload it it seems like it's taking forever. The user just sees the $ionicLoading overlay without information how long it will take or how much % are already uploaded.
The UX is bad because the user could assume, that the app froze or is in an endless loop and that it's just a bad app.
I have the following ideas but no idea if they are possible or
Is there a way in angular, cordova or ionic to get the browserinformation how much % are already uploaded?
Is there a way to get the current uploadspeed? I could get the size of my object by getting the length of my stringified JSON Object, divide it 1024 to get the kB. Then i would check the current uploadSpeed every second and add the current uploadspeed to a variable. With this information i could calculate the uploaded % approximately
JQuery ajaxForm Plugin?
Sounds like what you are looking for is progress events from XHR2.
Assuming your server is setup to handle XHR2 and return content-length, In plain JavaScript:
function upload(blobOrFile) {
var xhr = new XMLHttpRequest();
xhr.open('POST', '/server', true);
xhr.onload = function(e) { ... };
// Listen to the upload progress.
var progressBar = document.querySelector('progress');
xhr.upload.onprogress = function(e) {
if (e.lengthComputable) {
progressBar.value = (e.loaded / e.total) * 100;
progressBar.textContent = progressBar.value; // Fallback for unsupported browsers.
}
};
xhr.send(blobOrFile);
}
upload(new Blob(['hello world'], {type: 'text/plain'}));
Upload speed is also calculable using the information returned in the progress event, as you described.
As for this implementation in AngularJS/Ionic, it seems like this is a longstanding issue within the framework that $http doesn't really support progress events.
I have seen implementations that utilize a special angular directive written for this, or even utilize a jQuery file upload implementation.
I am working on a prototype Air for Android application that loads some remote swf files (all created and controlled by me) and I need to be able to pass a variable along to the remote SWF.
Here is a quick break down of how the process currently works:
The Android app, called Loader, is installed on the device.
Loader points to my server and finds Player.swf, which it loads into the main stage. Player.swf show a login screen.
When a user logs in correctly, Player.swf receives an XML response from an API with a list of remote SWF videos to display for the user. Player.swf loops through the XML list and plays each remote SWF, one after the other.
Now, so far, so good. The remote SWF videos load up and play perfectly. But I need to start picking up variables from Player.swf (pulled from the XML response) to use in the remote SWF (things like text strings, user ID's etc)
From all the searching I have done, I believe it is down to the Sandbox environment, as Loader is a compiled application and Player.swf + the remote SWF's are all coming off a server. However, now I am wondering, as Player.swf is the one that has the AS3 code to deal with the XML response, and that is located on the same server as the remote SWF's that are played (and therefore should be the same sandbox?)
I was able to pass a variable along using a URL parameter, but it only works locally and not when I use the Android application.
I found an old blog pot about Sandbox Bridges, but the example file download doesn't work anymore.
Can anyone point me in the right direction in using the parentSandboxBridge function/class to pass along a variable (all examples I have seen deal with passing functions and/or are coded for Flex)
I cannot post any code, as I simply have nothing to show for what I want to achieve (I'm totally stumped on this bit!)
EDIT: I managed to get parameters to pass along (even though it worked locally before, it didnt work with the Loader app > Remote Player > Remote SWF animation combo)
I had to set the application domain context inside Player.swf (seeing as it is on the same server as the remote swf animations, not sure why I needed to do this)
But oddly, I cannot use parent or root to pickup variables?
I've tested this on HTC Desire running android 2.2.2 and it works what I've put in comment thus the answer is as follows:)
your host needs to define similar:
var params:URLVariables = new URLVariables();
params["string"] = "ala ma kota";
params["number"] = 1979;
var request:URLRequest = new URLRequest();
request.data = params;
request.url = "urlVarsReader.swf";
var loader:Loader = new Loader();
loader.load(request);
addChild(loader);
then loaded content (here urlVarsReader.swf) can read them as follows:
var output:TextField = new TextField();
addChild(output);
output.border = true;
output.width = 320;
output.height = 240;
output.wordWrap = true;
output.multiline = true;
var p:Object = getParams(this.loaderInfo);
for (var name:String in p)
{
output.appendText(name + " = " + p[name] + "\n");
}
I have a wrapper method for reading params:
public function getParams(li:LoaderInfo):Object
{
try
{
var params:Object = li.parameters;
var pairs:Object = {};
var key:String;
for(key in params)
{
pairs[key] = String(params[key]);
}
}
catch(e:Error)
{
return {error:e.getStackTrace()};
}
return pairs;
}
best regards
EDIT:
This works only with local content fails with SecurytyError e.g. SecurityError: Error #2070: Security sandbox violation: caller http://example.com/urlVarsReader.swf?string=ala%20ma%20kota&number=1979 cannot access Stage owned by app:/loadwithparams.swf
I'm currently developing and I came up to the following question:
How can/should I store basic information, such as unlocked items and levels between devices.
Of course, I store them within preference files.
But what if the user buys a new phone and wants to continue playing on there?
(Shared-)Preferences won't be duplicated.
Thus, is there any (from google) recommended way (cloud?) to solve this issue?
I'm no expert, but the way I do this is with an online SQL database. Have the device connect to it, and save/load any data you need. I know this will not work with files, only text-based information. I also do this using Flash. If this is your route I can share some code.
Ok, so you need 3 things if you are doing this in flash: Flash (duh), PHP files, and a SQL server (I have a MYSQL server).
How it will work is flash will load php, php will do it's thing (like making changes to the database) and spit back it's results to flash.
Let's say we want to get all usernames from the database.
Database
The table name is called "Players" with a single column called "u_id"
Flash
var loader:URLLoader; //makes a loader that will "load" the php page
public function DoIt()
{
loader = new URLLoader();
loader.addEventListener(Event.COMPLETE, loaderComplete);
loader.load(new URLRequest("http://yourDomain.com/thePHPFile.php?arg=42"));
}
public function loaderComplete(e:Event):void
{
var returnXML:XML; //we are assuming the return values are in xml format, trust me, this might be easier
for each (var ListItem:XML in returnXML..Users) //you might need to change this "users" to be the root of the xml file
{
var userName:String = ListItem.u_id.toString();
//you can do what you want with the data here
}
}
PHP
Now it might not be required, but I try to make sure the PHP files and the DB is on the same host, so that I can do "localhost" as the host name
<?php
$argumentVar = $_GET["arg"]; //not using this, just showing it's possible
$connect = mysql_connect("localhost", "db_username", "db_password");
if(!$connect)
{
die("error connecting to DB" . mysql_error());
}
$db_selected = mysql_select_db("the_name_of_the_db", $connect);
if(!$db_selected)
{
die("error selecting db" . mysql_error());
}
$table_name = "Players" //this is the name of your database
$sql = "SELECT * FROM $table_name;"; //not sure if you need ending ';'
$dbresult = mysql_query($sql, $connect); //this is where all your data comes into
//and now to save all the information to a xml file
$doc = new DOMDocument('1.0');
$root = $doc->createElement('root');
$root = $doc->appendChild($root);
while($row = mysql_fetch_assoc($dbresult))
{
$occ = $doc->createElement($table_name);
$occ = $root->appendChild($occ);
foreach ($row as $fieldname => $fieldvalue)
{
$child = $doc->createElement($fieldname);
$child = $occ->appendChild($child);
$value = $doc->createTextNode($fieldvalue);
$value = $child->appendChild($value);
}
}
$xml_string = $doc->saveXML();
echo $xml_string;
?>
Ok, go play with that, it should start you on the right path.
i think the best way would be to have an online database be maintained which is regularly updated with the latest information for every registered user. So instead of storing everything locally it can be stored on a remote server and sent to the user as and when required. so as long as the user owns the account created by himself for the application, no data will be lost.