How to Store User Preferences in an Adobe Flex/AIR Application - apache-flex

What's the standard way of storing user preferences in a Flex application for AIR? I need to store simple parameters like lists of recently opened files, window positions and sizes etc.

my favorite way to do it is to combine a VO with LSO (local shared object). If you have a LOT of settings, this doesn't work too well. The advantage is that you get a strongly typed settings object which is therfore bindable and has code introspection and completion.
The cool thing is it's only about 5 lines of code to manage an LSO. In addition, it's also pretty easy to manage a local encrypted store if you want to store any sensitive data.
registerClassAlias("SettingsVO",SettingsVO); //This lets us store a typed object in LSO
var settings:SettingsVO;
var settingsSO = SharedObject.getLocal("settings");
//Check to make sure settings exist... if not, create a new settings object
if( settingsSO.size && settingsSO.data && settingsSO.data.settings){
settings = settingsSO.data.settings as SettingsVO;
}else{
settings = new SettingsVO();
}
Now if you want to save settings you simply do
settings.someSetting = "newValue";
settingsSO.data.settings = settings;
settingsSO.flush();
And this solution works on BOTH AIR and Flex in any browser. Newer browsers will delete this data when clearing cookies, so beware of that.

I think the most flexible way is to use a local SQLite database. It gives you unlimited, structured storage and encryption if needed. See Peter Elst's introduction if you want to get more info.

There is no "Standard" way, but there are a lot of approaches, all which boil down to storing the user's preferences, then loading them up at runtime based on some uesr credentials, then changing the app based on those preferences.
You may store them in a server side database, such as SQL Server or MySQL; then have flex call a service which queries the database and returns the data.
You may store them as Shared Objects, which are the Flash version of browser cookies. (I believe they work on AIR applications too). This can get cumbersome with lots of data.
You may store them in an XML document and throw them on the server. Conceptually this is not much different than storing them in a server side database; but could get very tedious if you have a lot of users.
You could also store them, in an AIR app, locally using a SQLite database. SQLite is an embedded database used in Adobe AIR.

I don't bother with any of the fancy stuff. Just store it in an xml file in the application directory. Done.
Filestream does throw an error if you try to store it using File.applicationDirectory. I just trick the program...
var trickFile:File = File.applicationDirectory;
var file:File = new File(trickFile.nativePath + mySettings.xml);
Air falls for it every time.

Related

SQLite-PCL : Accessing my own created table?

I'm referring to SQLite-PCL tutorial here: https://code.tutsplus.com/tutorials/an-introduction-to-xamarinforms-and-sqlite--cms-23020
I'm very new to SQLite, so I'm lacking in knowledge in lots of basic things - I have tried Googling, can't understand most of it.
Is the call to new SQLiteConnection actually opens up the database or just saying that "the road to the database has been established, whether you access it or not is up to you"?
How do I check if there's already existing database in the devices? And if there is, how do I access it? I have Googled this, but it all seems to be a bit extreme - can't I just call simple OPEN the database?
Is it okay to have multiple SQLiteConnection instances to the same database, if I can be sure that I'm not going to do multiple transaction at the same time?
After I have INSERT into the database, close the app, open up the app back - how do I make sure that there is database created in previous session? Any way to debug this? Because I have no idea if the database is there or not, and I don't know how to access it either..
SQLiteConnection returns a connection object that is used to make subsequent queries
use File.Exists to see if the db file is already present
Yes
Again, use File exists to see if the db file is physically present
Xamarin's ToDo sample provides a good overview of using SQLite with Forms.

How to access meteor collection through the database

I want to have my application's admin code hosted on a completely different app that shares the same database. However, that means that my collections are defined, at least in the code, in the global namespace of my main application and not my admin application. How can I access my collections, that are in the database, without having the global variables defined in a file shared between the meteor server/client? For reference, I am using this article as the idea to set up my admin tools this way. admin article
To simplify the problem, let's say you have:
two applications: A and B
one shared collection: Posts
one shared database via MONGO_URL
Quick and Dirty
There's nothing complex about this solution - just copy the collection definition from one app to the next:
A/lib/collections.js
Posts = new Mongo.Collection('posts');
B/lib/collections.js
Posts = new Mongo.Collection('posts');
This works well in cases where you just need the collection name.
More Work but Maintainable
Create a shared local package for your Posts collection. In each app: meteor add posts.
This is a little more complex because you'll need to create a pacakge, but it's better for cases where your collection has a model or other extra code that needs to be shared between the applications. Additionally, you'll get the benefits of creating a package, like testing dependency management, etc.
Each application will have its own code but will share the same mongo db. You'll need to define the same collections (or a subset or even a superset) for the admin app. You can rsync certain directories between the two apps if that makes that process either but there isn't anything in Meteor that will do this for you afaik.
Or you could share data between the two servers using DDP:
var conn = DDP.connect('http://admin-server');
Tracker.autorun(function() {
var status = conn.status();
if(status.connection) {
var messages = new Mongo.Collection('messages', {connection: conn});
conn.subscribe('messages', function() { console.log('I haz messages'); });
}
});
This creates a local collection named messages that pulls data from the "admin server" over DDP. This collection only exists in memory - nothing is created in mongo. You can do this on the server or client. Definitely not the best choice for large datasets. Limit the data transfer with publications.

Saving SQLite to Documents Folder

I need to pick an underlying method of saving data collected in the field (offline and remote locations). I want to use the HTML5 Database with SQLite but I can I pick the location? So far, I haven't been able to accomplish that. Here is some sample code I was using:
var dbName = "";
var Dir = blackberry.io.dir;
var path = Dir.appDirs.shared.documents.path;
dbName = path + "/" + "databasetest.db";
var db = openDatabase(dbName, '1.0', 'Test', 50 * 1024);
I used an "alert()" to see the file was "supposedly" created, but when I opened the folder in Explorer I cannot find it. Not really sure why and hense my question.
My application is for data entry, without getting into specifics, user may end up collecting a lot or little data. But I want some way of downloading the SQLite database?
Is this the intention of the SQLite database, or will I have to use another solution?
Thanks!
Chris
The Web SQL Database specification was designed for browsers where it would not have been appropriate to allow web pages to access arbitrary file paths.
The intended way to download data is to upload it to a web server in the cloud.
If you want to know the file name of your database, try executing the PRAGMA database_list. (Whether your app can access that path is a different question.)

Encrypted SQLite database cannot be attached: "Unable to open the database file"

The database can be open()ed using the same encryption key and it works fine. Tried with multiple encrypted databases - all can be opened, but not attached.
This works when encrypted and when not encrypted (bytearray is null):
connection.open(file, "create", false, 1024, bytearray);
This only works when not encrypted:
connection.attach("db" + newnum.toString(), file, new Responder(attachEncryptedSuccess, openEncryptedError), bytearray);
Any help is appreciated.
UPDATE:
Just found a strange pattern here:
It seems that if I create an encrypted database, and then create new databases and attach them, everything works fine.
The created files, after unloading, will only be properly opened using the command that they were initially created with. Therefore, the encrypted database that I created before using open() will only open with open() method. All the encrypted databases that were initially created using attach() will only be able to be opened using attach(). It also doesn't matter which database was open()ed first, aka which one is the main database. It can even be not encrypted.
This is something very strange. Is this a bug? Or am I doing something wrong here?
One gotcha that I ran into awhile ago, and it sounds like it might be impacting you. If you are creating both db's from AIR then this should work fine, however if you have created one with any external tool - generally most tools will default the PRAGMA ENCODING = UTF8. AIR, being Adobe, does things a little different than just straight up telling you that they create theirs UTF16-LE.
According to sqlite rules, differing encoding types cannot be attached one way or the other. One way to verify is to use sqliteman or some other sqlite editor to verify the pragma settings.
For me, I ended up having to start from a seeded db (empty databases -just the header- were over written by AIR) that was to be initialized from a template database. If I allowed AIR to create my starting db, it was set to UTF16 to which I could not attach a UTF8 template.

Adobe AIR HTTP Connection Limit

I'm working on an Adobe AIR application which can upload files to a web server, which is running Apache and PHP. Several files can be uploaded at the same time and the application also calls the web server for various API requests.
The problem I'm having is that if I start two file uploads, while they are in progress any other HTTP requests will time out, which is causing a problem for the application and from a user point of view.
Are Adobe AIR applications limited to 2 HTTP connections, or is something else probably the issue?
From searching about this issue I've not found much but one article did indicated that it wasn't limited to just two connections.
The file uploads are performed by calling the File classes upload method, and the API calls are done using the HTTPService class. The development web server I am using is a WAMP server, however when the application is released it will be talking to a LAMP server.
Thanks,
Grant
Here is the code I'm using to upload the file:
protected function btnAddFile_clickHandler(event:MouseEvent):void
{
// Create a new File object and display the browse file dialog
var uploadFile:File = new File();
uploadFile.browseForOpen("Select File to Upload");
uploadFile.addEventListener(Event.SELECT, uploadFile_SelectedHandler);
}
private function uploadFile_SelectedHandler(event:Event):void
{
// Get the File object which was used to select the file
var uploadFile:File = event.target as File;
uploadFile.addEventListener(ProgressEvent.PROGRESS, file_progressHandler);
uploadFile.addEventListener(IOErrorEvent.IO_ERROR, file_ioErrorHandler);
uploadFile.addEventListener(Event.COMPLETE, file_completeHandler);
// Create the request URL based on the download URL
var requestURL:URLRequest = new URLRequest(AppEnvironment.instance.serverHostname + "upload.php");
requestURL.method = URLRequestMethod.POST;
// Set the post parameters
var params:URLVariables = new URLVariables();
params.name = "filename.ext";
requestURL.data = params;
// Start uploading the file to the server
uploadFile.upload(requestURL, "file");
}
Here is the code for the API calls:
private function sendHTTPPost(apiFile:String, postParams:Object, resultCallback:Function, initialCallerResultCallback:Function):void
{
var httpService:mx.rpc.http.HTTPService = new mx.rpc.http.HTTPService();
httpService.url = AppEnvironment.instance.serverHostname + apiFile;
httpService.method = "POST";
httpService.requestTimeout = 10;
httpService.resultFormat = HTTPService.RESULT_FORMAT_TEXT;
httpService.addEventListener("result", resultCallback);
httpService.addEventListener("fault", httpFault);
var token:AsyncToken = httpService.send(postParams);
// Add the initial caller's result callback function to the token
token.initialCallerResultCallback = initialCallerResultCallback;
}
If you are on a windows system, Adobe AIR is using Microsofts WinINet library to access the web. This library by default limits the number of concurrent connections to a single server to 2:
WinInet limits the number of simultaneous connections that it makes to a single HTTP server. If you exceed this limit, the requests block until one of the current connections has completed. This is by design and is in agreement with the HTTP specification and industry standards.
... Connections to a single HTTP 1.1 server are limited to two simultaneous connections
There is an API to change the value of this limit but I don't know if it is accessible from AIR.
Since this limit also affects page loading speed for web sites, some sites are using multiple DNS names for artifacts such as images, javascripts and stylesheets to allow a browser to open more parallel connections.
So if you are controlling the server part, a workaround could be to create DNS aliases like www.example.com for uploads and api.example.com for API requests.
So as I was looking into this, I came across this info about using File.upload() in the documentation:
Starts the upload of the file to a remote server. Although Flash Player has no restriction on the size of files you can upload or download, the player officially supports uploads or downloads of up to 100 MB. You must call the FileReference.browse() or FileReferenceList.browse() method before you call this method.
Listeners receive events to indicate the progress, success, or failure of the upload. Although you can use the FileReferenceList object to let users select multiple files for upload, you must upload the files one by one; to do so, iterate through the FileReferenceList.fileList array of FileReference objects.
The FileReference.upload() and FileReference.download() functions are
nonblocking. These functions return after they are called, before the
file transmission is complete. In addition, if the FileReference
object goes out of scope, any upload or download that is not yet
completed on that object is canceled upon leaving the scope. Be sure
that your FileReference object remains in scope for as long as the
upload or download is expected to continue.
I wonder if something there could be giving you issues with uploading multiple files. I see that you are using browserForOpen() instead of browse(). It seems like the probably do the same thing... but maybe not.
I also saw this in the File class documentation
Note that because of new functionality added to the Flash Player, when publishing to Flash Player 10, you can have only one of the following operations active at one time: FileReference.browse(), FileReference.upload(), FileReference.download(), FileReference.load(), FileReference.save(). Otherwise, Flash Player throws a runtime error (code 2174). Use FileReference.cancel() to stop an operation in progress. This restriction applies only to Flash Player 10. Previous versions of Flash Player are unaffected by this restriction on simultaneous multiple operations.
When you say that you let users upload multiple files, do you mean subsequent calls to browse() and upload() or do you mean one call that includes multiple files? It seems that if you are trying to do multiple separate calls that that may be an issue.
Anyway, I don't know if this is much help. It definitely seems that what you are trying to do should be possible. I can only guess that what is going wrong is perhaps a problem with implementation. Good luck :)
Reference: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/FileReference.html#upload()
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/FileReference.html#browse()
Just because I was thinking about a very similar question because of an error in one of my actual apps, I decided to write down the answer I found.
I instantiated 11
HttpConnections
and was wondering why my Flex 4 Application stopped working and threw an HTTP-Error although it was working pretty good formerly with just 5 simultanious HttpConnections to the same server.
I tested this myself because I did not find anything regarding this in the Flex docs or on the internet.
I found that using more than 5 HTTPConnections was the reason for the Flex application to throw the runtime error.
I decided to instantiate the connections one after another as a temporally workaround: Load the next one after the other has received the data and so on.
Thats of course just temporally since one of the next steps will be to alter the responding server code in that way that it answers a request that contains the results of requests to more then one table in one respond. Of course the client application logic needs to be altered, too.

Resources