Conference App Secrets #1: EULA at first start of APP

October 9, 2013

Writing mobile applications in most cases you want to provide a EULA (End-User-License-Agreement) and the user has to agree to your conditions before using the App. Doesn’t matter if the app is free or not.

BlackBerry Jam Asia 2013 Conference APP

This year first time ever I developed the official BlackBerry Conference App for BlackBerry Jam Asia 2013 in Hong Kong. See this BlackBerry Developer Blog Post.

IMG_00000005

BlackBerry Jam Asia 2013 is a native BlackBerry 10 APP: Cascades / QML / C++ and can be downloaded from BlackBerry World for free.

It’s recommended to download the Conference App, so you can see how it all works in the context of a complex app.

The EULA is the first Dialog visible after downloading and opening the App, but it was the last part I developed ;-)

I allready had implemented an english version hard-coded, but for licenses it’s important that the user understands the content, so the license must be available in all languages.

Last – Minute hacking MUC -> DUBAI -> Hong Kong

I got this information just as I was boarding my flight from Munich to Hong Kong via Dubai. Fortunately the A380 from Emirates has WiFi on board.
So I could communicate and hacking the code.

In Dubai I had to wait some hours for my connection flight, so I did some last coding …

IMG_00000513

…and finally uploaded the App.

Thanks to BlackBerry I’m allowed to blog about the conference app, patterns used and tips or tricks. This article is the first one of a series.

Usecase

I want to give you some hints HowTo show a localized EULA at startup.

Extracting code from the Conference App into a easy-to-use sample will make it easy for you to use it.

eula1

Let’s take a look at the UI first

UI (QML)

We need a Dialog with a Title, the License Text and two Buttons: Agree / Don’t Agree. To solve this the easiest way, we create a Custom Dialog with some properties: alias properties to fill the fields and a result string.


Dialog {
  id: eulaDialog
  property string result
  property alias title: titleLabel.text
  property alias body: bodyLabel.text
  property alias button1: acceptButton.text
  property alias button2: cancelButton.text

The Dialog gets a Container with a solid background color, so nothing will shine through. The color depends from Theme:


Container {
  background: isDark() ? Color.Black : Color.White
  horizontalAlignment: HorizontalAlignment.Fill
  verticalAlignment: VerticalAlignment.Fill

To detect the Theme we use a function:


function isDark() {
  return Application.themeSupport.theme.colorTheme.style == VisualStyle.Dark
}

Inside the Dialog Container we have a Container for the Title and another Container for the content.

The Title Container in the sample app has a blue background – the conference app uses the special JAM color.

The Content is placed inside a ScrollView, so it doesn’t matter how long your license text is or if it’s running on a 720×720 Q10 or 720×1280 Z30.

At the bottom below the content the Accept and Cancel Buttons are placed. Hitting one of them sets the result string and closes the Dialog. Here’s the Accept Button:


Button {
  id: acceptButton
  text: qsTr("I Agree")
  layoutProperties: StackLayoutProperties {
    spaceQuota: 1
  }
  focusPolicy: FocusPolicy.KeyAndTouch
  onClicked: {
    eulaDialog.result = "ACCEPTED"
    eulaDialog.close()
  }
}

Complete sourcecode of this Custom Dialog is in

assets/EULADialog.qml

The logic to show the Dialog or not is inside the

assets/main.qml

which is a TabbedPane in ConferenceApp and a simple Page in the sample App.

At first we have to add the Custom Dialog as an attachedObject:


attachedObjects: [
  EULADialog {
    id: eulaDialog
    property bool firstRun: true
    onClosed: {
      if (eulaDialog.result == "ACCEPTED") {
        app.setEulaAccepted()
        // do your normal startup stuff now
        return
      }
    if (firstRun) {
      noEulaDialog.exec()
      return
    }
    Application.requestExit();
  }
},
SystemDialog {
  id: noEulaDialog
  title: "EULA License"
  body: qsTr("You must accept the EULA in order to use the App.") + Retranslate.onLanguageChanged
  confirmButton.label: qsTr("OK") + Retranslate.onLanguageChanged
  confirmButton.enabled: true
  cancelButton.enabled: false
  onFinished: {
    eulaDialog.firstRun = false
    eula()
  }
},
..... more
]

As you can see there’s a second Dialog, a SystemDialog – this Dialog is used if the user doesn’t accept the license. There’s only one chance to retry – if second time the license was not accepted, the App is closed:

Application.requestExit();

If the EULA is ACCEPTED we call a method from C++:

app.setEulaAccepted()

If the EULA is accepted, a value was inserted into Settings, so next time no EULA was displayed.

Now let’s take a look HowTo open the EULA Dialog. At the bottom of main.qml as part of the onCreationCompleted slot:


onCreationCompleted: {
  if (app.showEula()) {
    eulaTimer.start()
  } else {
    // do your normal startup stuff now
    doItAgin.visible = true
  }
}

We ask the C++ application if the EULA dialog must be opened:

app.showEula()

If the EULA must be opened a little trick:

While testing the Conference App on different devices with different OS we found out that an older 10.1 OS Version has some problems opening the Dialog direct from the onCreationCompleted{}. So we’re doing this async and start a single-shot QTimer, which then opens the dialog.

This QTimer was also attached as an object:


attachedObjects: [
  .......
  QTimer {
    id: eulaTimer
    interval: 500
    singleShot: true
    onTimeout: {
      eula()
    }
  }
]

onTimeout calls a function:


function eula() {
  var data = app.eulaContent()
  eulaDialog.title = data.title
  eulaDialog.body = data.body
  eulaDialog.button1 = data.button1
  eulaDialog.button2 = data.button2
  // now it's safe to open the Dialog
  eulaDialog.open()
}

We get the localized content from C++

app.eulaContent()

The content is a QVariantMap, so can directly be used as a Javascript Object and we set the values of alias properties.

Finally starting the App the first time we get immediately the Custom Dialog:

IMG_00000001

Above you see a localized Dialog with german Title and german Button texts. Starting the Conference App you’ll get a real EULA License text.

If the user doesn’t agree, this SystemDialog appears exactly one time and opens the EULA DIalog again:

IMG_00000002

Business Logic (C++)

Now let’s see what happens at C++ side.

Inside the constructor of applicationUi.cpp the QTimer must be registered as type, so QML knows it:


qmlRegisterType<QTimer>("my.library", 1, 0, "QTimer");

also we need the ‘app’ context property:


qml->setContextProperty("app", this);

Setting the context property isn’t enough – we must tell Qt that some of our methods can be invoked. This is done in the applicationUi.hpp Headerfile:


Q_INVOKABLE
bool showEula();

Q_INVOKABLE
QVariant eulaContent();

Q_INVOKABLE
void setEulaAccepted();

Back to the .cpp file and take a deeper look at these methods.


bool ApplicationUI::showEula() {
  QSettings settings;
  if (settings.value(SETTINGS_KEY_EULA_ACCEPTED).isNull()) {
    return true;
  }
  return false;
}

showEula() uses QSettings to see if the EULA was already opened. QSettings is a simple way to persists values in a local secure filestore.

You’ll find the settings file from TargetFileSystemNavigator – View in your Momentics IDE inside the sandbox of your app:

settingsfile

this is the path to the settings file:

data/Settings/<your-vendor-name>/<your-app-name>.conf

Opening the settings file you’ll find this entry if the EULA is accepted:

[General]
eula_read=true

here’s HowTo set the value:


void ApplicationUI::setEulaAccepted() {
  QSettings settings;
  bool accepted = true;
  settings.setValue(SETTINGS_KEY_EULA_ACCEPTED, QVariant(accepted));
}

Hint: If you want to test again if the EULA Dialog will be displayed, delete the Settings File from TargetFileSystemNavigator.

Now the last missing piece is getting the localized EULA.

Here’s the project structure from sample app:

eula sample project

All EULA texts are contained inside a JSON file:

assets/app_data/eula.json

The structure of this JSON is easy to understand:


[
  {
   "locale":"pl",
   "title":"AKCEPTACJA LICENCJI:",
   "body":"………… Lorem",
   "button1":"Zgadzam się",
   "button2":"Nie zgadzam się"
  }
,...
]

A JSON Array containing JSON Objects, where each JSON Object has as index the “locale” property (‘en’, ‘de’, ‘pl’,…) and properties for “title”, “body”, “button1″ and “button2″

Working with JSON in Cascades Apps is really easy. Here’s the code HowTo read this JSON Array from assets/app_data into a QVariantList:


QVariantList ApplicationUI::readEulaFromJson() {
  JsonDataAccess jda;
  QVariantList eulaList;
  QString eulaFilePath;
  eulaFilePath = QDir::currentPath() + "/app/native/assets/app_data/eula.json";
  if (!eulaFile.exists()) {
    qDebug() << "no eulaFile file found in assets - using english";
    return eulaList;
  }
  bool ok = eulaFile.open(QIODevice::ReadOnly);
  if (ok) {
    eulaList = jda.loadFromBuffer(eulaFile.readAll()).toList();
    eulaFile.close();
  } else {
    qDebug() << "cannot read eulaFile file: " << eulaFilePath;
  }
  return eulaList;
}

As soon as you got the list, you can search for the current locale. If an entry is found give this back.

If no entry found check for the first 2 characters only: the language. Per ex. ‘de_DE’ and ‘de_AT’ are valid locales for german language in Germany (DE) or Austria (AT), so if ‘de_DE’ not found, we look for ‘de’. If again no entry found, we use ‘en’ – english as default.

See the details in applicationUi.cpp:


QVariant ApplicationUI::eulaContent() {
...
}
QVariantMap ApplicationUI::euladoc(const QString& locale) {
...
}

Don’t forget

Please don’t forget to import the libraries in QML


import bb.system 1.0
import my.library 1.0

Also don’t forget to add the libraries into your .pro file_

LIBS += -lbbsystem -lbb -lbbdata

Summary

from this sample you have learned

  • HowTo use QSettings to persist values
  • HowTo access JSON data files
  • Communicate between C++ and QML
  • Use of QTimer in QML
  • Writing a Custom Dialog in QML
  • Using a SystemDialog in QML

Download and Discuss

The Sample APP is available at GitHub Open Source (Apache 2 License): https://github.com/ekke/cascades_eula

I also created a Thread in the Forums, where you can ask or discuss this.

Have fun with the sample app and copy/paste what you need to implement EULA Dialog into your own apps.

Trackbacks and Pingbacks:

  1. Secrets of the BlackBerry Jam Asia Conference App, Part 1: Custom Credentials Dialog «BlackBerry Developer Blog - November 26, 2013

    […] looking to implement a Login to start your app, you can implement your Login logic similar to the EULA license dialog. I have extracted the code to do a Custom Login into a sample app. It is now available at […]

  2. Secrets of the BlackBerry Jam Asia Conference App, Part 1: Custom Credentials Dialog | SDK News - November 30, 2013

    […] looking to implement a Login to start your app, you can implement your Login logic similar to the EULA license dialog. I have extracted the code to do a Custom Login into a sample app. It is now available at […]

  3. Conference App Secrets Part 3: JSON vs XML | BlackBerry Developer Blog - January 10, 2014

    […] In Part 1 I demonstrated How to dispay a EULA at the first start of the application. […]