SystemDialogControl
Usually dialogs are created in accordance to the app's design. This means that dialogs are also built up by several graphic nodes such as all other elements of the application. In some situations, e.g. when querying, if an app should be closed, real system dialogs are preferred. The Murl Engine provides the possibility to display a simple system dialog and to query the corresponding user input via its ISystemDialogControl
interface.
If a ISystemDialogControl
is provided on the platform, a ISystemDialog
object can be created via CreateSystemDialog()
. A dialog is displayed via the Open()
method and can be closed via Close()
.
For our example we use the 9 slice button from the previous tutorial and create three button instances.
<Instance graphResourceId="package_main:init_button_9s"/> <!--Draw Buttons--> <Instance buttonId="button01" text="Show Dialog" posX="-100" posY="0" graphResourceId="package_main:graph_button_9s"/> <Instance buttonId="button02" text="+" posX="-170" posY="-100" sizeX="70" sizeY="70" graphResourceId="package_main:graph_button_9s"/> <Instance buttonId="button03" text="NEW" posX="-30" posY="-100" sizeX="70" sizeY="70" graphResourceId="package_main:graph_button_9s"/>
In the header file, we create member variables for the buttons and a pointer to store for the ISystemDialog
object.
Logic::ButtonNode mButton01; Logic::ButtonNode mButtonPlus; Logic::ButtonNode mButtonNew; ISystemDialog* mSystemDialog;
The mSystemDialog
pointer is initialized to 0 in the constructor.
App::SystemDialogLogic::SystemDialogLogic(Logic::IFactory* factory) : BaseProcessor(factory) , mSystemDialog(0) { }
In the OnInit
method, the Logic::ButtonNode
member variables are initialized with the references of the button nodes. Additionally, a ISystemDialog
object is created and the object pointer is stored in the mSystemDialog
member variable.
Bool App::SystemDialogLogic::OnInit(const Logic::IState* state) { state->GetLoader()->UnloadPackage("startup"); Graph::IRoot* root = state->GetGraphRoot(); AddGraphNode(mButton01.GetReference(root, "button01/button")); AddGraphNode(mButtonPlus.GetReference(root, "button02/button")); AddGraphNode(mButtonNew.GetReference(root, "button03/button")); if (!AreGraphNodesValid()) { return false; } Logic::IDeviceHandler* deviceHandler = state->GetDeviceHandler(); if (deviceHandler->IsSystemDialogControlAvailable()) { mSystemDialog = deviceHandler->CreateSystemDialog("Test Dialog Title", "Test Dialog\nChoose Action\n3rd Line Message", "CANCEL", "OK"); } state->SetUserDebugMessage("SystemDialog Init succeeded!"); return true; }
We also pass the title and the info message of the dialog as parameters to the CreateSystemDialog()
method.
The created ISystemDialog
object has to be destroyed (released) with DestroySystemDialog()
. This can be done in the OnDeInit
method.
Bool App::SystemDialogLogic::OnDeInit(const Logic::IState* state) { Logic::IDeviceHandler* deviceHandler = state->GetDeviceHandler(); deviceHandler->DestroySystemDialog(mSystemDialog); deviceHandler->DestroyEMail(mEMail); deviceHandler->DestroyUrlRequest(mUrlRequest); return true; }
The dialog should be opened when we click on Button01 or when we press the D key. The dialog window is automatically closed when we click on a dialog button. Additionally we implement an auto-close-timer which closes the dialog automatically after a timeout of five seconds.
//SystemDialog if (mSystemDialog != 0) { static Double autoCloseTime; if (!mSystemDialog->IsOpen()) { if (mButton01->WasReleasedInside() || deviceHandler->WasRawKeyPressed(RAWKEY_D)) { mSystemDialog->Open(); autoCloseTime = 5; } } else { autoCloseTime -= dt; if (autoCloseTime < 0) { mSystemDialog->Close(-1); } }
With GetClickedButtonIndex()
or GetButtonLabel()
we can find out onto which button was clicked.
if (mSystemDialog->WasClosed()) { SInt32 clickedButtonIndex = mSystemDialog->GetClickedButtonIndex(); Debug::Trace("Clicked Button Index: "+Util::SInt32ToString(clickedButtonIndex)); if (clickedButtonIndex < 0) Debug::Trace("Titlebar close button pressed or time out close!"); else Debug::Trace("Clicked button label: "+mSystemDialog->GetButtonLabel(clickedButtonIndex)); }
We use the mButtonPlus
button in order to add another button to the dialog.
- Note
- Caution! On the Android operating system the maximum number of visible dialog buttons is limited to three.
The button mButtonNew
destroys the current ISystemDialog
object and creates a new one.
if (!mSystemDialog->IsOpen()) { if (mButtonPlus->WasReleasedInside()) { mSystemDialog->AddButton("EXTRA-"+Util::UInt32ToString(mSystemDialog->GetNumberOfButtons())); } if (mButtonNew->WasReleasedInside()) { deviceHandler->DestroySystemDialog(mSystemDialog); mSystemDialog = deviceHandler->CreateSystemDialog("Test Dialog Title", "New Dialog", "OK"); } } }
If there is more than one button in the dialog, the order of the buttons depends on the current platform on which the application runs. The button 0 is always on the position on which the negative button (typically "CANCEL") would usually be located. The CreateSystemDialog("Test Dialog Title", "Test Dialog\nChoose Action\n3rd Line Message", "CANCEL", "OK");
call would provide the following results on different platforms:
WebControl
The IWebControl
provides basic web features:
- Opening a website in the default browser
- Sending emails
- HTTP POST and HTTP GET calls
Again we use some additional 9 slice buttons and ButtonNode
member variables to trigger the different IWebControl
functions.
Opening a Website in a Browser
The method OpenUrlInSystemBrowser()
with the web address as parameter can be used to open a website.
//WebControl if (mButtonBrowser->WasReleasedInside()) { if (deviceHandler->IsWebControlAvailable()) deviceHandler->OpenUrlInSystemBrowser("https://murlengine.com"); }
The entered web address is opened in the default browser.
Sending Emails
In order to send emails we first need to create an IEMail
object. To reference this object we define an IEMail* mEMail;
pointer in the header file and initialize it to 0 in the constructor.
The CreateEMail()
method creates an IEMail
object and returns a pointer to the object. With the Send()
method we open the email client and the email can be sent by the user.
// Create and send Email if (mButtonEmail->WasReleasedInside()) { if (deviceHandler->IsWebControlAvailable()) { mEMail = deviceHandler->CreateEMail("My Subject", "My Message", "office@murlengine.com"); if (mEMail != 0) { mEMail->AddToRecipient("konrad@zuse.com"); mEMail->AddCcRecipient("alan@turing.com"); mEMail->AddCcRecipient("ivan@sutherland.com"); mEMail->AddBccRecipient("harry@nyquist.com"); mEMail->AddBccRecipient("john@neumann.com"); Debug::Trace(t+": Start Send"); mEMail->Send(); } } }
The created IEMail
object has to be destroyed (released) via DestroyEMail()
! We release the object as soon as the sending process has finished. The current state can be queried directly from the IEMail
object through one of the following methods:
// Destroy Email Object if (mEMail != 0) { if (mEMail->WasSent() || mEMail->WasSaved() || mEMail->WasCancelled() || mEMail->WasRejected()) { deviceHandler->DestroyEMail(mEMail); } }
In order to track the status we display the current status as debug message:
// Print Email status if (mEMail != 0) { if (mEMail->IsInQueue()) Debug::Trace(t+": IsInQueue"); else if (mEMail->IsSending()) Debug::Trace(t+": IsSending"); else if (mEMail->WasSent()) Debug::Trace(t+": WasSent"); else if (mEMail->WasSaved()) Debug::Trace(t+": WasSaved"); else if (mEMail->WasCancelled()) Debug::Trace(t+": WasCancelled"); else if (mEMail->WasRejected()) Debug::Trace(t+": WasRejected"); else Debug::Trace(t+": Unknown Status"); }
Typically, the output looks as follows:
- Note
- Caution! The
WasSaved()
,WasCancelled()
andWasRejected()
methods give an accurate result only with iOS. All other platforms return forWasSent()
true as soon as the email was successfully handed over to the email client. A verification, if the user has actually sent the message or if the email was e.g. discarded, is not possible on any other platform than iOS.
HTTP POST & HTTP GET
The IWebControl
provides with the method CreateUrlRequest()
the possibility to create an IUrlRequest
object which can be used for HTTP calls. To reference the IUrlRequest
object we define an IUrlRequest* mUrlRequest;
pointer in the header file and initialize it to 0 in the constructor.
In the OnInit
method we create an IUrlRequest
object and store the pointer of the object in the mUrlRequest
member variable.
mUrlRequest = deviceHandler->CreateUrlRequest();
The created IUrlRequest
object has to be destroyed (released) with DestroyUrlRequest()
! This can again be done in the OnDeInit
method.
deviceHandler->DestroyUrlRequest(mUrlRequest);
In order to send an HTTP request with HTTP GET, the SendGet()
method can be used. The HTTP address is passed as parameter.
if (mButtonHttpGet->WasReleasedInside()) { mUrlRequest->SendGet("https://murlengine.com"); Debug::Trace("URL request HTTP GET Start: '%s'", mUrlRequest->GetUrlString().Begin()); }
Of course the HTTP address can also contain HTTP GET parameters, e.g.
mUrlRequest->SendGet("https://murlengine.com/news/?murlpage=news&murllang=en");
The status of the HTTP request can be queried through the following methods:
//URL Request if (mUrlRequest != 0) { if (mUrlRequest->IsPending()) { Debug::Trace("URL request is pendind. Received %d bytes.",mUrlRequest->GetCurrentDataSize()); } else if (mUrlRequest->WasRejected()) { Debug::Trace("URL request was rejected."); } else if (mUrlRequest->WasFinished()) { UInt32 receiveSize = mUrlRequest->GetCurrentDataSize(); Debug::Trace("URL request has finished. Received %d bytes", receiveSize); UInt32 limitSize = receiveSize; const UInt32 LIMIT = 1024; if (limitSize > LIMIT) limitSize = LIMIT; String response = mUrlRequest->GetResponseData().GetString(limitSize); Debug::Trace("Response Content (first %d bytes):\n%s", limitSize, response.Begin()); }
The size of the received data can be queried through GetCurrentDataSize()
and the actual data through GetResponseData()
. The example code above outputs the first 1024 bytes as debug message.
Typically, the output looks as follows:
In order to send a HTTP request via HTTP POST, the SendPost()
method can be used. The HTTP address, the post data and the content type are passed as parameters.
if (mButtonHttpPost->WasReleasedInside()) { //early version of URL Encoding with space is + Data postData("Name=Jonathan+Doe&Age=23&Formula=a+%2B+b+%3D%3D+13%25%21"); //URL Encoding with space is %20 //Data postData("Name=Jonathan%20Doe&Age=23&Formula=a%20%2B%20b%20%3D%3D%2013%25%21"); mUrlRequest->SendPost("https://murlengine.com/xtest.php?id=42", postData, "application/x-www-form-urlencoded"); Debug::Trace("URL request HTTP POST Start '%s'", mUrlRequest->GetUrlString().Begin()); }
In the example code above the following parameter-value-pairs are transmitted:
For transmission, all data first have to be URL encoded (URL encoding also known as Percent encoding; see en.wikipedia.org/wiki/Percent-encoding). The encoded string then looks as follows:
This simple HTML/PHP script can be used to display the received data:
Typically, the output looks as follows: