Home » Posts tagged 'app inventor'

Tag Archives: app inventor

Multiple ORDER BY and massive UPDATE for fusiontables

Introduction

This tutorial explains how to overcome some Fusiontables current shortcomings, more precisely the issue 584 and issue 417 which affect the following:

  • Order by multiple fields: Currently the ORDER BY clause admits specifying only one column. We’ll add support for multiple columns
  • Massive update: Currently an UPDATE sentence admits specifying only one row, indicating the ROWID in the WHERE clause. We’ll add support for specifying a condition with the same format as any SELECT sentence and therefore with the possibility of updating several rows with a single sentence
  • Massive delete: Same limitation and same solution as the previous point

Additionally, in the Further ideas chapter we’ll mention some possible extensions for the service to include some other functionalities and we’ll see how to create a multipurpose service that provides 3 important services in a single application: the enhanced Fusiontables from this tutorial, a mail sending functionality and the typical custom TinywebDB.

This tutorial can be seen as a continuation of the post Fast & Safe Fusiontables using AI where we explained how to set up a service to access fusiontables and how to call it from App Inventor. The present tutorial has independent sources but some explanations are not repeated. Therefore, for an introduction to the service infrastructure you may prefer to start with a look at first part. Actually in this second part I’ve changed the code quite a lot in order to include the new functionality in a smarter way and also to provide a web interface. After this restyling, there are not many reminiscences left from ShivalWolf’s project (http://ai.kittywolf.net/index.php/WolfFUSIONdb), but the core infrastructure from our service is still based on his excellent job.

What are we going to build

Our goal is to set up a service extending fusiontables functionalities as explained above and also to provide a couple of ways to test the service: from AppInventor and from a Web interface:

fusion_12

Both interfaces work in the same way:

  • Introduce an SQL sentence for reading or manipulating a fusiontable
  • Press a button to execute the sentence
  • Show in the screen the results returned by our service

They also provide some samples to test the service with a very simple fusiontable. In the AppInventor app just select one of the four options available in the list picker Examples, whereas in the web interface you can press on the Syntax link and then the Try it link.

The sources from both interfaces are provided below, so it should not be difficult for you to adapt your own applications and access the service in a similar way. Anyway, in the above-mentioned tutorial there’s an explanation about how to adapt an AppInventor application.

The syntax allowed is the same standard fusion tables syntax except for the following extensions:

  • ORDER BY: The standard clause only accepts one field whereas our service admits several numbers separated by “,” corresponding to the desired ordering columns. The data is returned as a JSON string in the form:

[[row1_field1  row1_field2 …] [row2_field1  row2_field2 …] …]

A current limitation of this function is that all columns are ordered alphabetically regardless of their type. For example, if we order by a numeric column with values (1, 2, 10) the results will be (1, 10, 2)

ExampleSELECT * FROM TABLE_ID ORDER BY 1, 0

[Try it] (this test will access to a temporary service. Please make just a short try and then set up your own service as explained below)

  • UPDATE: The standard clause only accepts updating a single row through the ROWID specified in the WHERE clause, whereas service admits updating several fields in a single sentence by specifying a condition with the same syntax as any SELECT sentence:

ExampleUPDATE TABLE_ID SET Inventory = 357 WHERE Product = ‘Black Shoes’

[Try it] (it will try to update one row but will fail because this is a Google sample fusiontable for which we have no writing privileges)

  • DELETE: The standard clause only accepts deleting a single row through the ROWID specified in the WHERE clause, whereas service admits deleting several fields in a single sentence by specifying a condition with the same syntax as any SELECT sentence:

ExampleDELETE FROM TABLE_ID SET Inventory = 357 WHERE Product = ‘Black Shoes’

[Try it] (it will find one row to delete but won’t do so because of writing privileges)

Installation

Here are the sources for our project:

For a detailed explanation of installation steps you can look at the first part. As a summary, after setting up both projects you need to configure the following:

  • AppEngine: Edit the main.py file and replace the contents of the variables google_user, google_password, api_key and AuthKey:

############ SETTINGS ############
google_user = “me@gmail.com#indicate here the gmail address for your app engine account
google_password = “yourpassword#put your gmail password here (I suggest using 2 factor auth and making a separate password for this app
api_key = “yourkey#put the API key from Google API console
AuthKey = “randomkey#make this match the authkey in your code (provides limited protection to your interface to fusion tables)

  • AppInventor: In the blocks code modify the variables marked with a comment:

USR_KEY: indicate the same value as in the main.py file (see AuthKey above)

SERVICE_URL: the URL where you’ve deployed the service

You can also inform an SQL sentence into the  TextBoxQuery and the ID of a fusiontable in the TextboxTableId. This way, you can test with your own data instead of the sample.

How it works

We’re just going to see the part of the service that takes care of the enhanced functionality. You can find some more hints about the whole code in the first part, and if you’re interested in further explanations you can leave a comment that I’ll be pleased to answer.

The idea for providing the extended functionalities is to perform some actions in the server side in order to complement the work from the database. Concretely we’ll follow 3 steps:

  • Parse the input sentence in order to detect if any extended functionality is required
  • Call the standard fusiontable service
  • Perform the extra work required by extended functionalitaties, i.e. reorder the rows for extended order by and execute a loop of sentences for extended update/delete

Or course these extra actions will not run as fast as if the work were performed by the database, but according to my benchmarks results are acceptable, at least for my requirements. And they have no comparison with the cost we would incur if we had to do the same job in App Inventor (see image below).

So let’s see how to implement the functionalities in the server. We’ll start presenting three auxiliary functions and then see the algorithms for both the order by and the update/delete.

Auxiliary functions

  • sql_parse: takes care of parsing the input SQL sentence and returning the information about the enhanced functionality required:

Type of action (0 – standard, 1 – update, 2 – delete, 3 – order by)

New SQL sentence (or the original one if no enhanced functionality is required)

Identifier of the table we’re working with

SET clause (in case the sentence is an UPDATE)

WHERE clause (if any)

Special ORDER BY (if any): List of column numbers

URL access method (always GET except for standard UPDATE / DELETE

We’re not going to see the parsing algorithm because it consists of simple string manipulations. Indeed it could be improved in order to provide better information in case of wrong parameters.

  • sql_exec: executes an SQL sentence received as a parameter by calling the fusiontables API. Results are returned in CSV format.

We’re not going to see this code either since it uses the standard mechanism to work with URL by means of the urllib and urllib2 functions. Some more details were provided in part 1.

  • doRender: presents the output for the web interface, using the method explained in the excellent book Using Google App Enginese from Charles Severance.

ORDER BY

We’re trying to add support for ordering data by more than one field. Since the standard component is not doing it for us, we could consider 3 alternative methods

  1. .Let the database do the job. The idea is adding to the table a calculated field with a formula to achieve that this field corresponds to the actual ordering we’re intending to obtain. For example, if we want to order by two numeric fields A and B and we know B is lower than 1000, we could use the formula ‘(1000 * A) + B’. You can see an example of this approach in the post A multilingual & multidevice template (part 2).
  2. Let the client do the job. In our case, we should program an ordering algorithm in AppInventor.
  3. Let the server do the job. After receiving the results from the database, reorder the rows before sending them to the client

The first option would probably be the most performant but it’s not pleasant to modify the database for our convenience (probably in many cases we won’t have authorization to do it). Another limitation is that such an ordering would be fixed, i.e. we wouldn’t be able to establish dynamically the order criteria (unless we created a calculated field for each combination).

The second approach is really complicated to achieve with AppInventor. We should implement an ordering algorithm such as the bubble sort, which seems to me as a fascinating but really hard task (and very likely to result in poor performance).

So we’re going to use the third approach. Since we’ve got a service that captures the database results and sends them to the client, it’s easy to “intercept” these results and work on them. Luckily Python provides multiple ways to sort lists (see for example this site). Among these methods I was looking for a solution accomplishing two goals:

  • Good performance
  • Support for a variable number of keys

After searching inspiration on the web (see for example this post) I found no exact solution, but after combining ideas I arrived to an extremely simple solution:

if sqltype == 3: #ORDER BY
resp = sorted(resp, key = itemgetter(*orderby))

Python’s magic! Just a single line and our rows are reordered by the columns specified. Really simple and performant because sorting by key encompasses processing each row only once (i.e. as much calls to itemgetter as rows returned by the sentence).

UPDATE / DELETE

Here the idea is to perform several SQL sentences (one for each row meeting the filter specified in the WHERE clause). In this case I don’t see how could we solve it in the database, so there are two options remaining:

  1. Let the client do the job. Start with a query to obtain a list of ROWID’s meeting the filter specified in the WHERE clause, and then program a loop that performs an UPDATE / DELETE for each row
  2. Let the server do the job. The same SELECT and loop as in the first option, but without need for the intermediate information to travel between server and client

In this case I wouldn’t say the first option is out of question, because I actually used it in an app when I did not count on the current service. It was a really hard job taking into account that answers from fusiontables are asynchronous. I would not do it again but if you’re interested in see how I solved it, here is how it looked like:

fusion_16

Letting aside the difficulty, the client side solution has the problem of performance. As shown in next image, it involves about double network traffic than our server-side solution. Moreover the additional messages are from the android device, i.e. the most costly.

fusion_13

Imagine what would happen if we were to update 1000 rows rather than 3!

So let’s see how to program the update/delete loop in the server. This time it’s somewhat more than a single line, but not much more difficult:

if sqltype == 1 or sqltype = 2: #UPDATE or DELETE
success = 0
processed = 0
prefix = (‘UPDATE ‘ if sqltype == 1 else ‘DELETE FROM ‘)
for row in resp:
processed += 1
sql = ‘%s %s %s WHERE ROWID = \’%s\” % (prefix, table, updateset, row[0])
csvfile, err = sql_exec(sql, token, “POST”)
if not err:
success += 1
err = ‘Found: ‘ + str(processed) + ‘, Processed: ‘ + str(success)

Testing application

User interface

We’ll create a simple interface to test the service:

fusion_14

  • TextboxQuery: Contains the SQL sentence to be sent. It can be typed directly or filled automatically using the listpicker
  • ListpickerExamples: Shows a list of predefined examples (order by, update and delete). After choosing one of them, the textbox is replaced by the corresponding sentence
  • TextboxTableId: It’s used to generate the sample sentences. The identifier from this textbox is merged into the generated sentence
  • ButtonExecute: Calls the service
  • LabelLog: Shows the results
  • Web1: The Web component we’ll use to call our service
  • Clock1: A timer to measure the response time of both accessing methods

Code blocks

fusion_15

The code is quite simple, but some explanations about it can be found in part 1.

Further ideas

The whole idea of our service is to perform some work in the server side to provide functionalities not currently supported by fusiontables. So the same solution can be considered for any other shortcoming that affects us. A good way to start could be extending the syntax for the WHERE clause. For example:

  • ‘ROWID IN (list)’. Requested in issue 417 (and quite easy to implement)
  • GROUP BY: Requested in issue 584 (python is also likely to come to our help. See for example this post)
  • <Condition1> OR <Condition2>
  • <Condition1> AND <Condition2>

Of course some of these extensions may not be trivial to implement but they will always be faster than in the client side.

Another idea you might have if you’ve arrived up to here is that, once overcome the fear to customize the server side, we can profit to provide other services in the same application (remember Google AppEngine’s limitation to 10 free services). In my case, I use the following functionalities:

In order to combine these services, you must merge the code into a single project. You can see an example in the How to send direct mails tutorial and a discussion of whether it’s a good idea to group services in the post Cerntralized or Decentralized services?.

Finally, there are a couple of limitations I intend to overcome in the future. On the one side, differentiate the column type in the order by (e.g. order numbers by its ordinal value rather than as strings). On the other side, it seems the ClientLogin is somewhat outdated and might be discontinued so the service should be adapted to Oauth2.

Fast and safe fusiontables with App Inventor

Introduction

Fusiontables are the mechanism provided by App Inventor to operate on spreadsheet-like data. The component works out-of-the-box and is extremely simple to use, so if you’re new to App Inventor it’s highly advisable that you get familiarized with it following for example the  pizza party tutorial from the AI website. However, once overcome this initial level, you’ll discover some annoying inconveniences in the AI component:

  • Security: You need to grant public access to fusiontables in order for App Inventor to be able to access them
  • Performance: The response takes a while to arrive and the message “Fusiontables: processing query…” is not the kind of experience you want to give to the user while he’s waiting
  • Functionality: There are some syntax limitations that make it quite burdensome to perform some typical access operations (such as a performing a massive UPDATE or estalishing an ORDERBY using more than one field)

This tutorial explains how to set up a service to access fusiontables improving the first two issues (security and performance). The functionality enhancements will be explained in the second part. It’s also important to note that the service deployed in this first part is compatible with the AI fusiontables component whereas the service from the second part has some slight differences in the format of both input parameters and return value.

The core of the solution we’re going to explain is based on this ShivalWolf’s project, but I decided it could be helpful to write this post in order to complement Shiva’s excellent job with some points:

  • Explain how to migrate existing applications to the new method
  • Compare the service with the standard fusiontables from AI
  • Add some functionalities
  • Create a multipurpose service providing 3 important services in a single application: the enhanced Fusiontables from this tutorial, a mail sending functionality and the typical custom TinywebDB.

This first part covers the first two points.

What are we going to build

In this first part we’ll develop and deploy into App Engine a service that allows accessing fusiontables and then we’ll create a simple screen to access a fusiontable using both our new service and standard component from App Inventor:

fusion_01

Both access methods return the same contents because our web component works in a very similar way as the standard component. Very similar means that it allows accessing to data by means of the same SQL-like syntax. Therefore, if you have a working application and wish to adapt it to the new service, you just need to add a web component to the screen (informing its URL as https://yourservice.appspot.com/fusiontable) and make the following adaptations in the code blocks:

  App Inventor standard component New method
Component Fusiontable Web
How to send the SQL Sentence Fusiontable.Query Web.PostTextWithEncoding
Return event Fusiontable.GetValue Web.GotValue
How to parse the return List from CSV table Web.JsonTextDecode

See the code below for further details about these adaptations.

Installation

Fusiontables web service (App Engine)

    1. Download App Engine for Python at http://code.google.com/appengine/. After installing it, run the GoogleAppEngineLauncher by clicking its icon
    2. Download these sources . It is a zip file containg the source code for the Fusiontables web service
    3. Unzip the downloaded zip file. It will create a folder named EnhancedFusionAE_light . You can rename it if you want but then you’ll have to change it also in the app.yaml file
    4. Edit the main.py and replace the contents of the variables google_user, google_password, api_key and AuthKey as explained below
    5. In the GoogleAppEngineLauncher, choose File | Add Existing Application. Browse to set the Path to the folder you just unzipped.
    6. [Optional] Click the Run button to launch a test web service on your local machine.
    7. In the GoogleAppEngineLauncher, choose Deploy

############ SETTINGS ############
google_user = “me@gmail.com#indicate here the gmail address for your app engine account
google_password = “yourpassword#put your gmail password here (I suggest using 2 factor auth and making a separate password for this app
api_key = “yourkey#put the API key from Google API console
AuthKey = “randomkey#make this match the authkey in your code (provides limited protection to your interface to fusion tables)

Client side (App Inventor)

    1. Download these sources and upload them into App Inventor
    2. Open the project with App Inventor. Inform the Text attribute in the TextBoxQuery component with a sentence in a SQL format, including the Id of your fusiontable. You can obtain it opening the table from Google drive and choosing the option About this table. The “Id” field shows the table key
    3. In the blocks code inform modify the initialization of the following variables (marked with a comment)

USR_KEY: indicate the same value you’ve informed in the main.py file (step 4 above)

SERVICE_URL: indicate the URL where you’ve deployed the service (typically something like https://yourapp.appspot.com)

How it works

The server side is a service that receives a SQL string using the syntax allowed by fusiontables and returns the data as a JSON string in the format:

[[row1_field1 row1_field2 …][row2_field1 row2_field2…]…] 

I’m not going to explain the python code (which is not mine), but just remark the key aspects from the main.py file.

  • We query if an authorization token exists and obtain it otherwise:

entry = db.GglQuery(“SELECT * FROM StoredDate WHERE tag = ‘token'”).get()
if entry:
token = entry.value
else:
token = ClientLogin().authorize(google_user, google_password)

  • We access fusion table data as if we were calling google’s URL with the SQL sentence as a post data, and adding the authorization token to the request header:

details = tag.split(“|||”)
URL = “https//www.google.com/fusiontables/api/query”
POST_DATA = urllib.encode({‘sql’: details[1]})
request = urllib2.Request(URL, data = POST_DATA)
request.add_header = (“Authorization”, “GoogleLogin auth=” + token)
request = urllib2.Request(URL, data = POST_DATA)
response = urllib2.urlopen(request)

  • Finally we return the string obtained from the response:

csvfile.write(rsponse.read())
reader = csv.reader(csvfile)
header = reader.next()
outvalue = json.dumps([row for row in reader])
self.response.out.write(outvalue)

Regarding security, we just need to grant access to the fusiontable to the user account we’ve informed in the properties (since this is the account sent to fusiontable request). Typically this account will be the administrator of your AppEngine application so you can restrict access only to administrator informing the value ‘login: admin‘ in the handlers section of app.yaml file:

handlers:
- url: /fusiontable
  script: main.app
  login: admin

That’s all regarding the service. The client side is simple to implement in App Inventor since the web component allows calling a web service and obtaining its return value as the parameter of the gotValue event. The easiest way to work with this data is converting it into a list using the json.decode function as shown below.

User interface

We’ll create an app that allows accessing a table using both the standard Fusiontable component and our service. It will look like this:

fusion_02

The graphical components are self-explanatory. The remaining components are:

  • FusiontablesControl1: The standard AI component we’ll use for comparison
  • Web1: The Web component we’ll use to call our service
  • Clock1: A timer to measure the response time of both accessing methods

Code blocks

The first thing to do is indicate the value for the two configurations variables:

  • SERVICE_URL: You must replace this variable by the URL where you’ve set up the service
  • USR_KEY: Replace it by the authentication key you’ve informed in the main.py file

fusion_03

We have also 4 additional variables:

  • lstResult: Used to store the list obtained from the response (in both access methods)
  • iRows: Contains the number of rows read, in order to show it in the output message
  • iBench1, iBench2:  Store the time before and after obtaining data in order to show the delay time in the output message

And here are the actual functions:

fusion_04

The right hand functions are those that call the standard component and therefore are not object of this tutorial. However they can be useful to see the differences among both access methods.

Our web component requires just two functions (one for the request and the other one to capture the response):

  • Button1,click: When we press the button, we access the fusiontable simply posting to our web service a string with two pieces separated by ‘|||‘:
    1. Authentication key
    2. SQL sentence
  • Web1.GotText: The response is obtained in JSON format, which can be easily converted into a list thanks to the useful JsonTextDecode function provided by the web component

Finally, though it’s not relevant for this tutorial, here’s the code used to show the results in the output message:

fusion_05

Summary

That’s it. If you’ve arrived up to here you should have available a service compatible with the AI fusiontables component, and a sample AppInventor application that can access to it.

So what have we gained? Well, first off security. It’s not necesssary anymore to grant public access to the fusiontable for AI to be able to access to it. There’s also a performance issue. Actually it’s difficult to demonstrate that the current version of our service is faster than the standard component since it will give different results at each attempt, but at least our component does not show the message “processing query” which is not obviously the best way to entertain user’s waiting time. You’ll probably prefer a nicer progress bar or even show no message since the user can continue working without need to wait for the answer. For example, in our sample app you can press the “Web component” button and start editing the textbox before the results arrive. This is the magic of event-driven programming (see this related nice article from Jos Flores).

However, as demonstrated in this snippet from Puravida, App Inventor’s web component would also allow us to overcome both performance and security issues. So why should we bother to deploy a service? Well, the reason is that such a service opens the door for some nice features that can only be achieved in the server side. Indeed in part two we’re going to modify this service in order to add support for some candy functionalities leading to significant gains in performance and code reduction. That’s when the job we’ve done up to now will get more sense!

How to send direct emails with App Inventor

Introduction

App Inventor provides an easy mechanism to send mails using the client installed in the device. It’s based on the startActivity component and is extremely simple to implement as explained in the App Inventor reference documentation. Unluckily using the device mail client is not adequate when the mail needs to be sent without the user noticing it, or at least with minimal interaction (maybe just a notification). For example, I recently created a Login Template where it was unsuitable to show the mail prior to sending it because it contains a secret password and hence only the email receiver should be able to read it.

Therefore we’ll show an alternative way to send mails without user interaction.

What are we going to build

Actually the solution is not original but rather provided in a project by ShivalWolf (see next chapter for more details). However this post adds some practical details that were necessary to integrate the solution in my app, and which I think will be common to most apps:

  • How to set up the mail sending service together with the TinyWebDB service. Since we’ll usually have an application service to provide de DB capabilities, it would be a pity to use a different one for sending mails (remember Google AppEngine’s limitation to 10 free services).
  • How to deal with security issues for the service, i.e. the ability to restrict its utilization to privileged users. This is probably more relevant for the TinyWebDB than for the mail sending itself.
  • Upgrade the service to Python2.7. For an explanation of the steps involved you may be interesed in this post: Python 2.7 migration demystified.

By the way when explaining the Web DB service we’ll also mention a useful trick to ease DB editing which you may not be aware of since the TinyWebDB describes an alternative way of doing it.

Installation

Mail sender service (App Engine)

Assuming we really intend to provide a shared service for both the TinyWebDB and mail sending, I recommend following these steps to install the solution:

  1. Create and deploy the TinyWebDB service as explained in the tutorial Creating a Custom TinyWebDB Service
  2. Download the files app.yaml and main.py and replace the files with the same name located in the folder where you downloaded the TinyWebDB.
  3. Edit the main.py and replace the contents of the variables EmailFrom and AuthKey as explained below
  4. Deploy again the application as in the first step (i.e. with the AppEngine console)

Client side (App Inventor)

Download these sources and upload them into App Inventor. You’ll realise there’s some functionality that is not explained in this tutorial because this is actually a more general application (which is explained in the Login template post). The part that concerns us is just the mail sending window (see below). Alternatively, since both user interface and code blocks are very simple, you may consider developing the client side from scratch.

How it works

The service deployed in this tutorial serves two purposes: unify both services (TinyWebDB and SendMail) and introduce security. We’ll just explain the small adaptations required for these two purposes since the services are fully explained in the above mentioned posts.

The combination of both services consists of merging the contents of the respective main.py files. This can be done for example starting from the TinyWebDB file and including into it the classes from SendMail. Additionally we’ll need to indicate the function associated to each GET request, i.e. modify the following line:

### Assign the classes to the URL’s
app = webapp2.WSGIApplication([(‘/’, MainPage),
(‘/storeavalue’, StoreAValue),
(‘/deleteentry’, DeleteEntry),
(‘/getvalue’, GetValue),
(‘/sendemail’, sendemail)],
debug=True)

Also in the main.py file we need to inform the configuration variables as indicated by ShivalWolf:

############ SETTINGS ############
EmailFrom = “yourmail#set this to a valid email name and address eg “ShivalWolf <me@domain.com>”
AuthKey = “yourapp#Set the Auth key This is just a random value that you set in your App Inventor code

Regarding security, configurations must be made in the app.yaml file, concretely informing the value ‘login: admin‘ in the handlers section to indicate the service can only be called by the administrator. However this alone would not work because this restriction would also prevent App Inventor to access the services, so we must specify that our services are excluded from this restriction:

handlers:
login: admin
– url: /getvalue
script: main.app
login: optional
– url: /storeavalue
script: main.app
login: optional
– url: /sendemail
script: main.app
login: optional

That’s all regarding the two purposes of this tutorial. However, since we’re modifying the app.yaml file, I’ll profit to mention there is a useful parameter that allows using the Datastore administration for App Engine.

builtins:
– datastore_admin: on

Among other things, this will allow us to consult and modify our database records from the App Engine administrative console using a more user-friendly interface than the one provided by the TinyWebDb service.

User interface

logintutorial_81

As stated above, we’re interested in just a small part of the app:

  • HiddenMail_SEND: A textbox containing the address where the mail must be sent
  • ButtonOK_SEND: The button that provokes the mail to be sent
  • WebMailSend: The web component that performs the actual job. No need to inform any attribute since it’s done by code

Code blocks

Here’s a screenshot of the core function, both for AI classic and AI2:

 

 

logintutorial_92

The first thing to do is indicate the value for the two configurations variables:

  • APPENGINE_ADDRESS: You must replace this variable by the URL where you’ve set up the service
  • AUTH_KEY: Replace it by the authentication key you’ve informed in the main.py file

After this, sending the mail consists simply in posting a string to our service, containing five pieces separated by ‘|||‘:

  1. Authentication key
  2. Email address
  3. Email subject
  4. Email body
  5. Indicator of attachment. It must be informed as no because I’ve removed this part from the service.

How to adapt to screen orientation in App Inventor

Introduction

NOTE:

This post was initially published for AppInventor classic, but currently sources are available also for AI2. However, I have not updated the comments and images because the code is roughly the same.

This post explains a mechanism to resize the visual components when the screen device changes its orientation (i.e. from vertical to horizontal or vice versa). The objective is to avoid that these components get out of view when the screen height or width is shorter than in the original design.

It’s important to remark that the technique explained in this post can be adapted to achieve other purposes such as those mentioned in the Further Ideas chapter:

  • Adapt the components size in order to fit in the screen for devices with different size
  • Provide multilingual capabilities to an app

What are we going to build

We’ll typically design our app with vertical orientation, so when the orientation changes we need to “flatten” the design. For example, the app we’re going to follow contains a menu screen designed in a vertical layout to show 4 square buttons and a small button below (see image), but when orientation changes this shape provokes the lower buttons to disappear from the screen. Our solution converts these buttons into rectangles in order for them to fit in the screen.

logintutorial_72

Of course this adaptation is not always necessary or desirable. Actually you’ll probably want to avoid it for textbox components because reducing its original height is not likely to be a good idea.

Instead of resizing components, we could consider the option of moving them (fpr example the central screen in the image would be perfectly valid if we were able to place the small red button in the empty right side zone). However this would be nearly impossible in AI because the components’ position attribute is not modifiable by code (it’s rather assigned automatically). Maybe a solution based on hiding and showing arrangements could be an idea to explore but I’m not sure this would really work.

How it works

The idea is to divide the screen in an imaginary grid, with each component being fully located in some of the resulting cells and taking into account both orientations. In our example the grid could have a size of 9 x 11 which would give the following appearance in both orientations:logintutorial_71

This division allows the algorithm to work out the X and Y position of each component by multiplying the left-top position within the grid by the width of each cell and the same for the Y position.

For example, if our screen size is 440 x 270, the button’s position would be:

  Cell Size Blue button Cyan button Green button Yellow button
Horizontal 30 x 40 (60, 80) (180, 80) (60, 240) (180, 240)
Vertical 40 x 30 (80, 60) (200, 60) (80, 240) (200, 240)

Installation

You can try the application by downloading these sources and uploading them into App Inventor. You’ll realise there’s some functionality that is not explained in this tutorial because this is actually a more general application (which is explained in the Login template post). The part that concerns us is just the menu window.

User interface

logintutorial_15 logintutorial_16

The components are very simple. No secrets with buttons: one for each option and an additional one for “Exit”. Labels are somewhat trickier because they’re used to leave some space between buttons. The TableArrangementMenu1 has 4 columns and 5 rows and the labels are positioned in:

  • Label1_3: Row 1, Column 3
  • Label_3_1: Row 3, Column 1
  • Label_5_3: Row 5, Column 3

To achieve the layout required, you can specify the desired height and width for these labels. However, these attributes will be overridden by code.

Code blocks

Variables

logintutorial_73

  • iScreenHeight, iScreenWidth: Store the screen height and width. They are updated at initialization time and each time the screen orientation changes
  • iHeight, iWidth: Auxiliary variables

Functions

logintutorial_74

  • Screen1.Initialize, Screen1.OrientationsChanged: These are the two events that concern us, as they are the points when the initialization of components size takes place by means of a call to the initMenusize function
  • initMenuSize: This is the actual function that places the components according to the screen current size. Since we cannot indicate the position, the secret consists in specifying the height and width for both the buttons and the spacer labels strategically placed between buttons (see above). The calculations should not be difficult to understand if we have the grid image in mind. Note that the example above was simplified so the numbers in the code are slightly different. For example the buttons occupy 3 x 4 cells (rather than 3 x 3) and the screen is divided in 13 x 11 cells (rather than 11 x 9) because we have some extra space occupied by a header bar. I advise you to experiment different combinations to fully understand the algorithm.

Further ideas

The work we’ve done in this tutorial can be a first step to achieve other useful functionalities. In this final chapter I expose a couple of such extensions that I’ve developed and are explained in this tutorial.

Adapt components size to different devices

Supporting different devices without having to create different versions of the app is probably a much more common requirement than adapting to orientation change. The good news is that our technique is quite close to the solution. For example, the menu built in this tutorial would be correctly displayed in a larger device such as a tablet. For the whole app to be smartly presented we must simply adapt the initMenuSize function in order to apply the same resizing technique to all of our compoments. Of course this would be much more tedious, so rather than explicitly indicating each component in the function, we must previously create a  list of components, and loop through this list in the initMenuSize function. This is not a five minutes task so please refer to the above mentioned tutorial for the details.

Multilingual capabilities

Similarly, if we intend our app to be able to “speak” several languages, we won’t obviously want to rewrite it for each supported language, but rather think of a means to modify the text of captions and labels at initialization time in order to show them in the language chosen by the user. Once again the idea is looping through all components in an initialization function, in order to set the caption corresponding to the current language. The captions mst be obtained from a list populated at design time and stored for example in a TinyWebDB component. Another fascinating project that deserves a tutorial on its own.

An optimal way to manage multiple windows in App Inventor

Introduction

NOTE:

This post was initially published for AppInventor classic, but currently sources are available also for AI2. Unless explicitly noted, comments and images correspond to the classic version but the code is roughly the same.

I have to admit that the method we’re going to explain is not so advantageous in AI2 because now it’s possible to edit several screens at development time, which was one of the two main weak points in AI classic multiscreen. Anyway, the drawback of not being able to share variable and methods remains, and I think that’s enough reason to use this method is most of our projects.

This post demonstrates a method for managing multiple windows in App Inventor based on hiding and showing arrangements from the same screen. I admit the title may be controversial, but I’m really convinced that this is the most effective way to manage multiscreen in the AI current state of art. Anyway I understand this is arguable so if you’re not comfortable with the title just think of this tutorial as an “alternative method to manage multiscreen” which you could consider in some of your apps.

What are we going to build

As we know, release 42 of AI added support for multiscreen, recognizing it as an absolute necessity for any app that goes beyond a simple prototype. So before release 42 we were forced to simulate multicreen by showing and hiding controls. And now I’m proposing to go back to those times (!). Well, let me clarify it. What I’m proposing to do is avoid creating a separate screen for every window you want to show. The reason is that current AI screens have two main drawbacks:

  • You cannot reuse anything among different screens. In other words, you cannot share functions and controls that are common to all screens (you’re forced to replicate them in all the screens). For variables this is somehow mitigated because it’s possible to pass values, but for functions it’s really tedious and a great source of quality problems. We’re still lucky to count on the excellent copier facility provided by the App Inventor Repository, but this helps only when creating the app and not for its further maintenance (which consumes 80% of development time in traditional environments and probably more in AI)
  • You cannot switch forms while testing in the development environment, so it’s not possible to test interaction among screens (you need to generate a version each time)

By including several windows in a single screen we avoid both problems: you can test the windows interaction while developing and you can have a shared version of variables, functions and components that are common to all windows. It has also drawbacks, from which I’d remark two:

  • The design task is more complicated because all the windows are mixed and it’s harder to see the real appearance of each one
  • You have to manage the transitions between screens (particularly the back button)

For the first issue I recommend to mark all windows as non-visible except the one we’re working on. This way we can concentrate successively in the different windows and work in the same conditions as if we were using separate screens. Hidden windows are not a problem for testing because they are appropriately shown by code (as we’re going to see).

Regarding the second issue, that’s precisely why I’ve written this post. We’ll see a method to handle windows transitions in a systematic way. It may appear complex but we must take into account that part of this code would be necessary anyway if we used separate screens. Moreover, it would be replicated in every screen. On the whole, the code for the same app would be considerably longer (I know it very well because the app was initially written with separate screens!).

How it works

The key for simulating the screen transactions is a well known pattern called stack (technically a LIFO stack, which stands for “Last In First Out”). For those who have been around the programming world for some years this pattern leads us back to the hard origins when we were forced to program in machine code primitive computers such as Sinclair Spectrum. The pattern idea is to pile up an undetermined number of events (in our case the windows shown) but at any point in time we need only to be aware of the last event. The solution consists in using an infinite stack (which in AI can be implemented with a list) where we add and remove elements, and maintaining a pointer (a numeric variable in AI) indicating which is the last element from the stack.

This is more clearly shown with an example. Imagine the user logs into the application, pushes the Register button and from here the Help button. Then he presses twice the back button which must bring him back to the login window. The following image depicts the contents of the stack after each event, the red element indicating the currently shown window:

logintutorial_61

As we can see, after pressing the back button we must simply remove the top element and get the new top in order to locate the window to show.

Installation

You can try the application by downloading these sources and uploading them into App Inventor. You’ll realise there’s some functionality that is not explained in this tutorial because this is actually a more general application (which is explained in the Login template post). The part that concerns us is the way to implement seven windows using just two screens.

User interface

Each window to show must be simulated by means of a TableArrangement component. If this kind of arrangement is not suitable for the layout required in our window we can simply add the right arrangement under the TableArrangement. For example, the following image shows a window that requires two arrangements (Horizontal and Table):

logintutorial_62

Therefore our graphical design task consists in distributing each component into the TableArrangement where it must appear. The Arrangements’ Width and Height attributes will typically be set to “Fill parent”.

Code blocks

CONSTANTS

logintutorial_63

  • SCR_LOGIN, SCR_REG, SCR_SEND, SCR_HELP, SCR_GOOGLE, SCR_MAIN: We associate a numeric value to each window in order to make the code more readable (e.g. the sentence “CurrentScreen = SCR_MAIN” is much easier to understand than “CurrentScreen = 1

VARIABLES

logintutorial_64

  • lstStack: List of open windows. This is the stack used to control the back navigation
  • iStackPointer: Pointer to the current screen within lstStack
  • lstScreens: List of TableArrangements used as containers to simulate windows. The fact that all elements in the list have the same type allows to make generic calls to obtain an element and show it, which is the key for the pushScreen, popScreen and ShowScreen auxiliary functions

INITIALZATION FUNCTIONS

logintutorial_65

  • Screen1.Initialize: The only meaningful part is the call to function initScreen and pushScreen. Other sentences are not used in this tutorial
  • initScreen: Initializes the lstScreens variable with the table arrangements used to simulate windows. The order is very important, because the table correspondiing to each window is located by position in this list. For example, to obtain the Register window we must use the sentence:

SELECT LIST ITEM lstScreens, SCR_REG

USAGE FUNCTIONS

The mechanism to open and close windows is as simple as it would be with screens. Here are a couple of examples:

logintutorial_66

  • ButtonRegister.click: Any action that requires opening a window needs just to call the pushScreen function passing as a parameter the constant value assigned to the window
  • Screen1.BackPressed: Call the popScreen in order to remove the top window from the stack and show it. If the stack is empty close the application

Of course for this simplicity to be possible we need the auxiliary functions popScreen, pushScreen and those related.

AUXILIARY FUNCTIONS

logintutorial_67

  • pushScreen: This function receives as a parameter the window to open. It adds the window to the stack, hides the current window and shows the new one using the ShowScreen auxiliary function
  • popScreen: Obtains the window that is on top of the stack and hides it. Then removes it from the stack, obtains the previous one and shows it using the ShowScreen auxiliary function. It returns the currently visible window
  • getCurrentScreen: obtains the screen that’s currently being shown (i.e. the one on top of the stack). This function is called from the initHelp function in order to initialize the help texts taking into account the current window. The function returns directly the iStackPointer value so it does not save space but using a function is smarter and eases evolution in case the stack implementation changes some day

logintutorial_68

  • ShowScreen: This function receives as a parameter the index of the window to show. It then retrieves the associated TableArrangement from the lstScreens list and makes it visible. That’s all regarding the graphical stuff, but this function is also the equivalent to the screen’s Initialize event so it’s the place to handle specific initialization events. In this case:
    • Enable a timer in case we’re showing the Google window (and disable it otherwise)
    • Hide the top bar in case we’re showing the Google or Help window
    • Show the Profile button only in case we’re showing the Main window
  • ShowMain: This function is called to show the main menu. An important aspect to remark is that once in this window the back button is not supposed to go to the previous window but rather to close the application, so the function empties the stack before pushing the Menu window into it.

A simple way to encrypt texts with App Inventor

Introduction

NOTE:

This post was initially published for AppInventor classic, but currently sources are available also for AI2. However, I have not updated the comments and images because the code is roughly the same.

This post explains a simple method to encrypt/decrypt a text using exclusively the functions provided by App Inventor, i.e. without need to call a specialized library. One of the advantages of using only client code is that it does not require an Internet connection to work.

An encryption algorithm is a function that applies a certain transformation (encryption) to an input string in order to obtain an output string which can afterwards be transformed again (decrypted) in order to get back to the original string. Encryption algorithms are usually public, but (in theory) only the encrypter system is able to perform the decryption because the transformation algorithms use a secret key only known by the system.

The reason for building the solution explained in this tutorial is that I could not find one that met my requirements. As explained in this post from Google Groups, we can consider 3 ways to implement encryption in AI:

  1. Call a server side library using the StartActivity component. The AI Extensions mentioned in the same post seem to be an excellent choice for this purpose
  2. Use javascript in the client side. This is based on the clever idea of downloading such scripts in the first run as explained this post from Puravida
  3. Program your own algorithm using AI code blocks. One solution for this is the Encryption and Decryption v3 from Abacus apps.

I didn’t like the first solution because in my case I have the original text in the client side so it looks safer sending it already encrypted to the server. The second option meets this requirement but I was not comfortable with relying on javascript and additional downloaded files. So I decided to try the third approach, which is the one explained in this utorial.

Next question is why not the solution mentioned in the third point. Well, there are three reasons for that. The first one (and to be honest the real reason) is that this solution was not posted when I needed it. The second reason why my solution might still be useful is that it’s so simple that it has immediate response time whereas it seems the mentioned solution can take up to 22 seconds as explained in this post. The final reason is that this site intends to provide some instructive material that helps intermediate readers improve their skills on AI and development in general, and I hope the post can be useful in this sense regardless of whether the results is used.

That being said, if you’re lloking for a more sophisticated solution developed in App Inventor you should refer to the solution mentioned in the third point which will probably have more support and evolution than my humble contribution.

What are we going to build

The method is inspired in standard encryption principles but with several workarounds to overcome the lack of some functions in App Inventor. Actually it is simpler to program than to explain, so maybe you’ll prefer to skip next section and go directly to the code or to the example below. Anyway for those interested I’ll try to explain the theory behind this code.

How it works

We’re going to implement a symmetric encryption algorithm, i.e. one that uses the same key to encrypt and decrypt. A usual means to achieve this is the XOR function because it has the property that XORing a data twice with the same key results in the same data. The XOR function receives two arguments (in our scenario the input string and the secret key) and obtains the output by comparing each bit from the two arguments and generating a 1 for different bits and 0 otherwise. For example, if the input string is “ac” and the key is “12” we would do the following:

logintutorial_51

So the output string would be “PQ”. Now we’ll apply the same algorithm to obtain the original string:

logintutorial_52

Unluckily App Inventor does not provide the XOR function (see related issue) nor bitwise manipulation functions that would allow simulating it. So we’ll use a substitute mechanism: create a string containing all the possible characters of the input string, and substitute each character by the opposed in this string, i.e. substitute the first character by the last one, the second by the last but one, etc. An example is simpler to understand: if the allowed characters are “aeiouAEIOU” and the input string is “aA” then the output string will be “Uu”. Notice that if we apply the same transformation to the string “Uu” we obtain again the original “aA” string.  This way we have a reversible function like XOR, but with this alone our encryption would be very weak because anyone reading this tutorial (or with some imagination) would be able to decrypt it. That’s where the key comes into play. What we’ll do is “disorder” the allowed characters string by placing at the beginning the characters of the key. For example if the allowed characters are aeiouAEIOU and the key is “aEiO” we’ll generate the encrypter string “aEiOeouAIU

Installation

I have no isolated sources for this tutorial because its implementation is embedded into a more general application (a “Login template” that uses encryption to store passwords). So if you need to implement this algorithm you can either type the code looking at the images below or download the whole sources of the login template. By the way, if you’re interested in the template you can have a look at the Login template tutorial.

User interface

I won’t show the graphical part of this functionality because it’s mixed with other controls so it would be difficult to show separately the specific components. Moreover, the interface has no secret: if you want to a make a quick test you just need to create a screen with two textboxes (for the input string and the key), a button (to perform the encryption) and a label (to show the results).

So let’s go with the code!

Code blocks

Before seeing the encryption and decryption functions, let’s have a look at the required initializations:

logintutorial_93

Constants

  • ALPHANUMERIC_CHARS: A string composed by all the characters that can appear in the input string. You can add or remove characters if you wish to
  • CRYPTO_KEY: Your specific key. You SHOULD change its value. You can use only characters contained in the ALPHANUMERIC_CHARS variable. The string can have any length, but it’s advisable for it to have at least 10 characters. You can repeat the same character several times in the key, but only the first one will be taken into account

Variables

  • sEncrypterKey: contains the key string used by the encrypter and decrypter algorithms
  • sVal, sRet_xor: auxiliary variables

Initialization functions

  • InitCrypto: This function informs the sEncrypterKey variable with the same characters as the ALPHANUMERIC_CHARS variable but disordered (i.e. the characters from CRYPTO_KEY are moved at the beginning). The code consists of a simple loop through the CRYPTO_KEY characters in order to move them at the beginning. Notice the “if” line which is used to ignore any repeated characters

Main functions

logintutorial_94

  • encrypt , decrypt: Yes, as easy as that! The output string is simply obtained by applying the pseudoXOR function to the input string
  • pseudoXOR: The “reversing” function. It’s based on a loop that reads each character from the input string and locates its opposed character within the sEncrypterKey variable.

Further ideas

The algorithm explained in this tutorial is very basic. It would not be very hard for an expert to decipher the key if he was able to capture enough couples of encrypted-decrypted strings. If you want to sophisticate it but don’t wish to recur to the above-mentioned more complex solutions, you can extend the code in many ways. For example we could add a random number of characters at the beginning and at the end of the deciphered string in order to mislead hackers. In this case, for the decryption to work we should also include a couple of counters indicating where is the real string. This is better explained with an example:

DATA
Input string: dog
Key: sample
Allowed characters: abcdefghijklmnopqrstuvwxyz
Previous characters: 1
Ending characters: 5
ENCRYPTING STEPS
1) Disorder the allowed characters string in order to obtain the encrypter string: samplebcdfghijknopqrtuvwxyz
2) Reverse the input string: qbp
3) Add random characters: wqbppoldw
4) Add two counters at the beginning: amwqbppoldw (see next point to understand it)
DECRYPTING STEPS
1) Obtain the real string. “a” is the second character in the encrypter string and “m” is the third. This means the real string starts at position 2 and has a length of 3, i.e. it’s the string qbp
2) Reverse the string: dog

To implement this variation we should modify the encrypt and decrypt functions in the following way:

logintutorial_56

A Login template for App Inventor (part 2)


Code blocks

NOTE:

This post was initially published for AppInventor classic. Currently sources are available also for AI2 but unless explicitly noted comments and images correspond still to classic version since the app is roughly the same.

This post completes the tutorial for creating a login template. In part 1 we designed the user interface. Now we’re going to add the code necessary for it to work!

The following image depicts the blocks for all the functionalities:

logintutorial_31

The blocks editor layout intends to be organized in a way that facilitates its comprehension:

  • Constants: Variables with an initial value that does not change. According to the standard programming convention their name is fully written with capital letters and the character “_” is used to separate words.
  • Variables: Variables that change over time. They are typically initialized in the initializations functions (see below). By convention they have a prefix indicating their type (s: text, i: number, lst: list, b: boolean)
  • Auxiliary functions: General purpose procedures shared by different functionalities
  • General events: Events captured during the screen execution

  • One column for each of the functionalities: Login dialog, Register dialog, Send password dialog, Goggle login interface, Menu

Constants

logintutorial_32

  • C_BLUE, C_CYAN, C_ORANGE, C_GREEN, C_RED: Some customized colors for the menu. For example:

logintutorial_33

NOTE: These colors are taken from the palette defined in the google style guide for android

  • C_BLUE_SEL, C_CYAN_SEL, C_ORANGE_SEL, C_GREEN_SEL, C_RED_SEL: Some more colors, slightly lighter than the previous ones. They’re used to change the menu option color when pressed.
  • CLIENT_ID, CLIENT_SECRET, RESPONSE_OK, RESPONSE_DENIED: Constant values for the Google Login functionality explained in the tutorial How to redirect to the Google login.
  • SCR_LOGIN, SCR_REG, SCR_SEND, SCR_HELP, SCR_GOOGLE, SCR_MAIN: We associate a numeric value to each window. This is used in the screen management tutorial to make code more readable (e.g. the sentence “CurrentScreen = SCR_MAIN” is much easier to understand than “CurrentScreen = 1
  • DATA_PWD, DATA_MAIL, DATA_SENDMAIL: We associate a numeric value to the fields stored in the database. This is used to make the code more readable (e.g. when this information is stored in a list we can obtain “SELECT LIST ITEM list DATA_MAIL” which is easier to understand than “SELECT LIST ITEM list 2”). This way, to add a new field to the database we simply need to create a new constant with a significant name and assign to it the first available number.
  • ALPHANUMERIC_CHARS: This string contains all the valid characters for both username and password. It’s used for validating the entered values and also in the encryption algorithm explained in the post A simple way to encrypt texts with App Inventor.
  • APPENGINE_ADDRESS, AUTH_KEY: The authenticator key and address for the mail sending service. They MUST be informed with the right value as explained in the post How to send direct mails
  • CRYPTO_KEY: A key used to encrypt passwords. You should change it to a value that only you know

Variables

logintutorial_34

  • lstStack, lstScreens, iStackPointer: Variables used to control the back navigation (see the post An “optimal” way to manage multiple screens)
  • lstUserInfo: A list containing all the information from a user: password, email, sendmail
  • iScreenHeight, iScreenWidth, iHeight, iWidth: Variables that store the screen size. They are used to calculate dynamically the dimension of some components (for example when changing the screen orientation as explained in tutorial How to adapt controls appearance depending on the screen orientation)
  • sCode, sResponse, sToken, lstResponse: Used in the Google Login window
  • sEncrypterKey: A string used by the encrypter algorithm. It’s calculated at initialization time by merging the constants ALPHANUMERIC_CHARS and CRYPTO_KEY
  • sUser: Stores the user that’s currently logged in
  • bLogged: Indicator of whether the user is already logged. It’s used to decide the behaviour in some places (e.g. make visible the User label at the header screen)
  • iIndex, iVal, sVal: Auxiliary variables used in several places
  • sRet_checkPassword, sRet_isAlphanumeric, sRet_xor: Return values for functions

NOTE: The latter variables are not necessary anymore in the version for AI2 since return value is solved by means of local variables (see below the code for the checkUsername function)

Auxiliary functions

logintutorial_36

  • initMenuSize: Used to set the height and width of menu options depending in the screen size. It’s called when initializing the screen and also when changing the screen orientation. The code is explained in the post How to adapt controls appearance depending on the screen orientation
  • popScreen, pushScreen, getCurrentScreen, showScreen, showMain: Functions used to manage the screens. They are explained in the tutorial An ‘optimal’ way to manage multiple screens
  • initCrypto, encrypt, decrypt, pseudoXOR: Functions used to encrypt/decrypt passwords. They are explained in the tutorial A simple way to encrypt texts with App Inventor
  • initColors: Used to apply the customized colors to the components. It simply sets the right Backgroundcolor attribute to each component
  • initScreens: Initializes the variable lstScreens with the list of existing windows

logintutorial_35

  • initHelpLabels: This function is called every time the help button is pressed. It sets the text labels of the screen buttons according to the currently selected window (which is received as an input parameter)

logintutorial_37

  • checkUsername, checkPassword: Validation functions that enforce some rules regarding the username and password format. The code is self-explanatory
  • isAlphanumeric: Auxiliary function that returns true if all the characters of the string received are alphanumeric (i.e. exist in the ALPHANUMERIC_CHARS constant)

In this case let’s see the checkUsername function both in AI classic and AI2, since it’s a simple example that demonstrates how to make our code cleaner and safer thanks to local variables:

logintutorial_90 logintutorial_91

General events

logintutorial_41

  • BackPressed: Manages the behaviour of the “back” button in order to simulate the window closing, as explained in the post  An ‘optimal’ way to manage multiple screens
  • ScreenOrientationChanged: Resizes controls according to the current screen height and width, as explained in the post How to adapt to screen orientation
  • OtherScreenClosed: Restores the components’ colors since they may have changed to improve user feedback.

NOTE: AI does not provide visual feedback when a button is pressed. This is annoying specially if the action associated to the button takes some time to initiate because the user may not be sure of whether he’s really pressed the button. That’s why we improve his experience by playing a sound and changing the color of the pressed button

  • Initialize: Calls the initialization functions, shows the login window, obtains default values for some fields (username and password) and redirects to Google Login if this is the default option

Login window

logintutorial_42

  • ButtonLogin.Click: Queries TinyWebDB to verify that the username/password exist in the database.
  • TinyWebDB_LOGIN.GotValue: Obtains the information about the user by means of the getUserInformation function (explained below). If password is correct shows the menu window calling the ShowMain function explained in the tutorial  An ‘optimal’ way to manage multiple screens. Additionally, if “Remember” is checked, stores the values entered in the TinyDB so that they become the default options for the device.
  • ButtonGuest.Click: shows the menu window calling the ShowMain function explained in the tutorial An ‘optimal’ way to manage multiple screens. This function receives the user as a parameter, which in this case is informed with the special value “_guest”. In further places a comparison with this value is used to limit the available options (e.g. disable the option to modify the profile)

Register window

This window is used both for the initial register and to modify the profile. In the second case the username textbox is disabled. The code uses this condition to skip some controls in case we’re just modifying information.

logintutorial_43

  • ButtonRegister.Click: Shows the Register window using the PushScreen function explained in the post  An ‘optimal’ way to manage multiple screens.
  • ButtonOk_REG.Click: Validates that username and password format and queries TinyWebDB to verify the username does not already exist in the database
  • TinyWebDB_REG.GotValue: After some validations stores the information in the database and closes the window using the popscreen function explained in the post  An ‘optimal’ way to manage multiple screens.

logintutorial_44

  • GetUserInformation: Auxiliary function that receives as input parameter a string of data separated by the characters “|||” (i.e. the way it’s stored in the database) and returns a list with an element for each piece encountered (e.g. the input string “myPwd|||myMail|||1” would return a list of 3 elements: [myPwd] [myMail] [1] ). Additionally, this function decrypts the password
  • SetUserInformation: As opposite to the previous function, this one receives the information fields as parameters and returns a string in the format required to store the information in the database. It also encrypts the password

Forgot password window

logintutorial_45

  • ButtonForgetPWD.Click: Shows the Forgot Password window using the PushScreen function explained in the post  An ‘optimal’ way to manage multiple screens
  • ButtonOk_SEND.Click: Queries TinyWebDB to obtain the information about the username from the database
  • TinyWebDB_SEND.GotValue: Stores the information read into some hidden fields and calls the sendMail function. Finally closes the window using the PopScreen function
  • sendMail: This is the function that performs the actual job. It’s explained in the post How to send direct mails

Menu window

logintutorial_46

  • Button_1.Click, Button_2.Click, Button_3.Click, Button_4.Click: Open the second window passing as a parameter the logged user. It also changes the button to a somewhat lighter color
  • ButtonProfile.Click: Opens the Register window initializing previously the username textbox and disabling it. It also queries TinyWebDB to obtain the information about the username
  • TinyWebDB_Profile.GotValue: Calls the GetUserInformation function in order to convert the information read into a list, and then initializes the fields with this information.