SystemDialogControl
Normalerweise werden Dialoge passend zum grafischen Design der App gestaltet. Sie werden also wie alle anderen Elemente der Applikation aus Graphenknoten zusammengesetzt. In manchen Situationen, z.B. bei einer Abfrage ob die App tatsächlich beendet werden soll, ist aber der Einsatz von echten System-Dialogen gewünscht. Die Murl Engine bietet mit dem ISystemDialogControl
die Möglichkeit einen einfachen System-Dialog anzuzeigen und die entsprechende Benutzereingabe abzufragen.
Wenn auf der Plattform ein ISystemDialogControl
zur Verfügung steht, kann mit CreateSystemDialog()
ein ISystemDialog
-Objekt erzeugt werden. Mit der Methode Open()
wird der Dialog angezeigt und mit Close()
kann er wieder geschlossen werden.
Für unser Beispiel verwenden wir den 9-Slice-Button aus dem vorigen Beispiel und erzeugen drei Button Knoten.
<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 der Header Datei erstellen wir Membervariablen für die Buttons und einen Zeiger für das ISystemDialog
-Objekt.
Logic::ButtonNode mButton01; Logic::ButtonNode mButtonPlus; Logic::ButtonNode mButtonNew; ISystemDialog* mSystemDialog;
Im Konstruktor initialisieren wir den Zeiger mSystemDialog
auf 0.
App::SystemDialogLogic::SystemDialogLogic(Logic::IFactory* factory) : BaseProcessor(factory) , mSystemDialog(0) { }
In der OnInit
Methode werden die Logic::ButtonNode
Membervariablen mit den Referenzen der Buttonknoten initialisiert. Zusätzlich wird ein ISystemDialog
-Objekt erzeugt und der Zeiger auf das Objekt in der MemberVariable mSystemDialog
gespeichert.
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; }
Beim Erzeugen des Dialogs geben wir den Titel und die Nachricht für den Dialog gleich mit an.
Das erzeugte ISystemDialog
Objekt muss mit DestroySystemDialog()
auch wieder zerstört (freigegeben) werden! Dies wird in der OnDeInit
Methode erledigt.
Bool App::SystemDialogLogic::OnDeInit(const Logic::IState* state) { Logic::IDeviceHandler* deviceHandler = state->GetDeviceHandler(); deviceHandler->DestroySystemDialog(mSystemDialog); deviceHandler->DestroyEMail(mEMail); deviceHandler->DestroyUrlRequest(mUrlRequest); return true; }
Der Dialog soll geöffnet werden, wenn auf Button01 geklickt oder wenn die Taste D gedrückt wurde. Der Dialog wird automatisch geschlossen sobald auf einen Dialog-Button geklickt wurde. Zusätzlich implementieren wir einen AutoCloseTimer, der nach einem Timeout von 5 Sekunden den Dialog automatisch schließt.
//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); } }
Mit GetClickedButtonIndex()
bzw. GetButtonLabel()
kann herausgefunden werden, welcher Dialog-Button geklickt wurde.
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)); }
Den Button mButtonPlus
verwenden wir um dem Dialog einen neuen Button hinzuzufügen.
- Zu beachten
- Achtung! Bei Android ist die maximale Anzahl der sichtbaren Dialog-Buttons auf drei beschränkt.
Der Button mButtonNew
zerstört das aktuelle ISystemDialog
-Objekt und erzeugt ein neues ISystemDialog
-Objekt.
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"); } } }
Gibt es mehr als einen Button im Dialog, ist die Reihenfolge der Buttons von der aktuellen Plattform abhängig, auf der die Anwendung läuft. Der Button 0 wird immer an der Position angezeigt, an der normalerweise der negative Button (typischerweise "Abbrechen") liegen würde. Der Aufruf CreateSystemDialog("Test Dialog Title", "Test Dialog\nChoose Action\n3rd Line Message", "CANCEL", "OK");
liefert auf den unterschiedlichen Plattformen folgende Ergebnisse:
WebControl
Das IWebControl
-Objekt bietet grundlegende Web-Funktionen an:
- Öffnen einer Webseite im Standard-Browser
- Versenden einer Email
- HTTP POST und HTTP GET Aufrufe
Wir verwenden wieder mehrere 9-Slice-Buttons und ButtonNode
Membervariablen um die Code-Ausführung zu steuern.
Webseite im Browser öffnen
Um eine Webseite zu öffnen, genügt der Aufruf der Methode OpenUrlInSystemBrowser()
mit der Webadresse als Parameter.
//WebControl if (mButtonBrowser->WasReleasedInside()) { if (deviceHandler->IsWebControlAvailable()) deviceHandler->OpenUrlInSystemBrowser("https://murlengine.com"); }
Die angegebene Web-Adresse wird im Standard-Browser geöffnet.
Email senden
Um eine E-Mail verschicken zu können, muss zuerst ein IEMail
Objekt erzeugt werden. Für die Speicherung dieses Objekts definieren wir in der Header Datei einen Zeiger IEMail* mEMail;
und initialisieren diesen im Konstruktor auf 0.
Die Methode CreateEMail()
erzeugt ein IEMail
Objekt und gibt einen Zeiger auf das Objekt zurück. Mit der Methode Send()
wird dann der Email-Client geöffnet und das Email kann vom Benutzer abgesendet werden.
// 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(); } } }
Das erzeugte IEMail
-Objekt muss mit DestroyEMail()
auch wieder zerstört (freigegeben) werden! Wir geben das Objekt frei sobald der Sendevorgang abgeschlossen wurde. Der aktuelle Status kann mit folgenden Methoden direkt vom IEMail
Objekt abgefragt werden:
// Destroy Email Object if (mEMail != 0) { if (mEMail->WasSent() || mEMail->WasSaved() || mEMail->WasCancelled() || mEMail->WasRejected()) { deviceHandler->DestroyEMail(mEMail); } }
Um den Statusverlauf anzuzeigen, geben wir den aktuellen Status noch als Debug Meldung aus:
// 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"); }
Der typische Output dafür sieht folgendermaßen aus:
- Zu beachten
- Achtung! Die Methoden
WasSaved()
,WasCancelled()
undWasRejected()
liefern nur bei iOS ein akkurates Ergebnis. Alle anderen Plattformen liefern immer fürWasSent()
den Wert true zurück, sobald die Email-Nachricht erfolgreich an den Email-Client übergeben wurde. Eine Überprüfung ob der Benutzer die Nachricht tatsächlich verschickt hat oder die Email z.B. verworfen wurde, ist auf nicht iOS-Plattformen leider nicht möglich.
HTTP POST & HTTP GET
Das IWebControl
bietet mit CreateUrlRequest()
die Möglichkeit ein IUrlRequest
Objekt zu erzeugen, welches dann für einen HTTP Aufruf verwendet werden kann. Für die Speicherung des IUrlRequest
Objekts definieren wir in der Header Datei den Zeiger IUrlRequest* mUrlRequest;
und initialisieren diesen im Konstruktor auf 0.
In der OnInit
Methode wird ein IUrlRequest
Objekt erzeugt und der Zeiger auf das Objekt in der MemberVariable mUrlRequest
gespeichert.
mUrlRequest = deviceHandler->CreateUrlRequest();
Das erzeugte IUrlRequest
Objekt muss mit DestroyUrlRequest()
auch wieder zerstört (freigegeben) werden! Dies wird in der OnDeInit
Methode erledigt.
deviceHandler->DestroyUrlRequest(mUrlRequest);
Ein HTTP-Request mit HTTP GET kann mit der Methode SendGet()
ausgeführt werden. Als Paramter wird die HTTP-Adresse übergeben.
if (mButtonHttpGet->WasReleasedInside()) { mUrlRequest->SendGet("https://murlengine.com"); Debug::Trace("URL request HTTP GET Start: '%s'", mUrlRequest->GetUrlString().Begin()); }
Die HTTP-Adresse kann natürlich auch HTTP GET Parameter beinhalten. z.B.
mUrlRequest->SendGet("https://murlengine.com/news/?murlpage=news&murllang=en");
Der Status des HTTP-Requests kann mit folgenden Methoden abgefragt werden:
//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()); }
Die Größe der erhaltenen Daten kann mit GetCurrentDataSize()
und die eigentlichen Daten können mit GetResponseData()
abgefragt werden. Im Beispiel werden die ersten 1024 Bytes als Debug Meldung ausgegeben.
Der typische Output dafür sieht folgendermaßen aus:
Um einen HTTP-Request mit HTTP POST abzuschicken, kann die Methode SendPost()
verwendet werden. Als Parameter werden die HTTP-Adresse, die Post-Daten und der "Content Type" übergeben.
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()); }
Im oberen Beispiel werden die folgenden Parameter-Wert-Paare übermittelt.
Für die Übermittlung müssen die Daten zuerst URL-kodiert werden (URL Encoding oder auch Percent-encoding; siehe auch de.wikipedia.org/wiki/URL-Encoding). Der kodierte String sieht dann folgendermaßen aus:
Die folgende einfache HTML/PHP-Seite zeigt die empfangenen Daten an:
Der typische Output sieht dann folgendermaßen aus: