Sharing Files on Android or iOS from or with your Qt App – Part 2

by Ekkehard Gentz [Independent Software Architect, Consultant] (Qt Blog)

Please read Part 1 of this Blog series about sharing Files on Android and iOS where I wrote about my experiences how to share Files or Content from your Qt App with native Android or iOS Apps.

In the meantime I did some UI tuning and added more info to make it easier to understand and to manage some more use-cases, so expect some more parts of this series next weeks.

Also I found out that not all Android Apps are good citizens of ACTION_EDIT – Intents with ResultCode. After editing and save per ex. Google Fotos sends correct RESULT_OK where other Apps are giving you RESULT_CANCEL. For our workflows it‘s important to know if a File was modified, so as a workaround I store the last-modified-Timestamp before starting the Intent and compare with current Timestamp when Result was handled. All of this is done under the hood and you‘re always getting the correct result code back from EDIT Action.

Some preliminary notes

  • Try it out: all sources are available at GitHub
  • Permissions not checked in Example App: enable WRITE_EXTERNAL_STORAGE manually
  • Current release is for Target SDK 23 ! (Android 7 requires FileProvider – will add later)
  • All Use-Cases are implemented to be used x-platform – currently Android and iOS

If you‘re looking for an Android-only solution please also take a look at AndroidNative project.

Android / iOS

First part covers sharing Files from your App with other Apps on Android and iOS.

This Blog Post is about sharing Files with your Qt App on Android (iOS will follow soon):

  • Use your App as Share Target to receive Content or Files from other Android Apps
  • Impacts on „Sharing Files from your App with other Apps“ (see Part 1)

Intent Filter

To tell the Android OS what kind of Files our App can handle we must add an Intent Filter to AndroidManifest.xml:

<activity … >
        <!-- Handle shared incoming urls -->
        <intent-filter>
            <action android:name="android.intent.action.SEND"/>
            <category android:name="android.intent.category.DEFAULT"/>
            <data android:mimeType="*/*"/>
        </intent-filter>
        <intent-filter>
            <action android:name="android.intent.action.VIEW"/>
            <category android:name="android.intent.category.DEFAULT"/>
            <data android:mimeType="*/*"/>
            <data android:scheme="file"/>
            <data android:scheme="content"/>
        </intent-filter>
</activity>

Now the App can receive Intents for all kind of MimeType using ACTION_VIEW or ACTION_SEND.

Custom Android Activity

To be able to get the Intent we must add some code to our main Activity, so extend the default QtActivity. To do so create a QShareActivity.java at this location:

<project>/android/src/org/ekkescorner/examples/sharex

public class QShareActivity extends QtActivity
{
    @Override
    public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
    }
}

As next change the Name in AndroidManifest.xml:

<activity ... android:name="org.ekkescorner.examples.sharex.QShareActivity" ...>

Get the incoming Intent

Now the interesting part:
Our App can be opened by other Apps – how do we get the Intent data and read or copy the ‚foreign‘ File ?

This depends from current ApplicationState:
if the App is already running, Android OS will call onNewIntent() – if not our App will be launched and the Intent comes in from onCreate(). The best way is to check the Intent from both methods and then call another method to process the Intent.

Attention: there are some traps to watch !

To inform the UI about an incoming File as usual we want to use a SIGNAL.

If the App is already running we want to open our App exactly on the current Page. Imagine a DropBox-like App where you navigate deep into your File hierarchy and now you want to upload a new Presentation File into the current Folder, but you must edit the Presentation before. So you open PowerPoint or something else, create/edit the presentation and then want to share that File with your App. Of course the User wants to be exactly where he was before without the need to do deep File navigation again.

If our App will be launched by the incoming Intent, onCreate() was called from Android OS to deliver the Intent. Unfortunately at this moment the Qt App isn‘t ready and the SIGNAL will be lost. To avoid this don‘t process the Intent from onCreate() but remember that there‘s a pending Intent.

Later when the Qt App is ready check for pending Intents. HowTo know if the App is ready to process Intents ?

In our Example App we‘re checking the ApplicationState and first time the State becomes Active we ask for pending Intents.

In a real-life business App probably you must at first Login to your Server or get some data from Server, so you‘ll do the check for pending Intents later.

LaunchMode ‚singleInstance‘ vs ‚singleTask‘

As next I found out that the last opened Page wasn‘t shown – instead a white Screen comes up and an error was logged: ‚Surface1 null‘.

Reason was the Launch Mode. Most Android Intent examples are using ‚singleTask‘ so I also did it. Changing the LaunchMode to ‚singleInstance‘ in combination with ‚taskAffinity‘ works better for Qt Apps: now the App opens correct the last opened Page.

Here are the changes to AndroidManifest.xml:

<activity ... android:launchMode="singleInstance" android:taskAffinity="">

Some code snippets:

QShareActivity.java:

public class QShareActivity extends QtActivity
{
    public static boolean isIntentPending;
    public static boolean isInitialized;
    public static String workingDirPath;

    @Override
    public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
          // now we're checking if the App was started from another Android App via Intent
          Intent theIntent = getIntent();
          if (theIntent != null){
              String theAction = theIntent.getAction();
              if (theAction != null){
                  // delay processIntent();
                  isIntentPending = true;
              }
          }
    }
    // if we are opened from other apps:
    @Override
    public void onNewIntent(Intent intent) {
      super.onNewIntent(intent);
      setIntent(intent);
      // Intent will be processed, if all is initialized and Qt / QML can handle the event
      if(isInitialized) {
          processIntent();
      } else {
          isIntentPending = true;
      }
    }
    public void checkPendingIntents(String workingDir) {
        isInitialized = true;
        workingDirPath = workingDir;
        if(isIntentPending) {
            isIntentPending = false;
            processIntent();
        }
    }
    // process the Intent if Action is SEND or VIEW
    private void processIntent(){
      Intent intent = getIntent();
      // do something with the Intent
    }
}

Check ApplicationState in ApplicationUI.cpp:

#if defined(Q_OS_ANDROID)
void ApplicationUI::onApplicationStateChanged(Qt::ApplicationState applicationState)
{
    if(applicationState == Qt::ApplicationState::ApplicationActive) {
        // if App was launched from VIEW or SEND Intent
        // there's a race collision: the event will be lost,
        // because App and UI wasn't completely initialized
        // workaround: QShareActivity remembers that an Intent is pending
        if(!mPendingIntentsChecked) {
            mPendingIntentsChecked = true;
            mShareUtils->checkPendingIntents(mAppDataFilesPath);
        }
    }
}
#endif

Process the incoming Intent

Now let‘s take a look at processIntent() from QShareActivity.java to see how to read the File from the other Android App.

We‘re listening for VIEW and SEND Intent Actions. The ways to get the Uri are different:

Uri intentUri;
String intentScheme;
String intentAction;
if (intent.getAction().equals("android.intent.action.VIEW")){
       intentAction = "VIEW";
       intentUri = intent.getData();
} else if (intent.getAction().equals("android.intent.action.SEND")){
       intentAction = "SEND";
        Bundle bundle = intent.getExtras();
        intentUri = (Uri)bundle.get(Intent.EXTRA_STREAM);
} else {
        return;
}

As next we must check the Scheme to know if it‘s a ‚file‘ or ‚content‘ Scheme.

// content or file
intentScheme = intentUri.getScheme();
if (intentScheme == null){
      return;
}
if(intentScheme.equals("file")){
      // URI as encoded string
      setFileUrlReceived(intentUri.toString());
      // we are done Qt can deal with file scheme
      return;
}
if(!intentScheme.equals("content")){
        return;
}

To get the real FilePath from a Content Uri isn‘t so easy. Found many complicated examples and finally I got a great solution to extract the absolute FilePath from the Content Uri 🙂

Please take a look at QSharePathResolver.java:

<project>/android/src/org/ekkescorner/utils/

public class QSharePathResolver {
    public static String getRealPathFromURI(final Context context, final Uri uri) {
                // ...
        }
}

QSharePathResolver.java checks if the content Uri comes from

  • ExternalStorageProvider
  • DownloadsProvider
  • MediaProvider: Images, Video, Audio
  • GoogleFotosUri

and tries to calculate the real path. Now it‘s easy to get the File from an Intent providing a Content Uri:

filePath = QSharePathResolver.getRealPathFromURI(this, intentUri);
setFileUrlReceived(filePath);
// we are done Qt can deal with file scheme
return;

If the Uri couldn‘t be resolved from QSharePathResolver.java we try to read the InputStream. Please take a look at QShareUtils.java: createFile(ContentResolver cR, Uri uri, String fileLocation)

filePath = QShareUtils.createFile(cR, intentUri, workingDirPath);
setFileReceivedAndSaved(filePath);

01_new_intent_from_other_app

Ok – we received the File from our custom Activity – how can we provide this to our QML UI where we‘re waiting for a SIGNAL from C++ ?

From Java (Activity) to C++ to emit the SIGNAL for QML

The methods setFileUrlReceived() and setFileReceivedAndSaved() are native methods:

public class QShareActivity extends QtActivity
{
    // 'file' scheme or resolved from 'content' scheme:
    public static native void setFileUrlReceived(String url);
    // InputStream from 'content' scheme:
    public static native void setFileReceivedAndSaved(String url);
}

Native Methods are implemented in C++ via JNICALL – see all details in AndroidShareUtils.cpp:

#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT void JNICALL
  Java_org_ekkescorner_examples_sharex_QShareActivity_setFileUrlReceived(JNIEnv *env,
                                        jobject obj,
                                        jstring url)
{
    const char *urlStr = env->GetStringUTFChars(url, NULL);
    Q_UNUSED (obj)
    AndroidShareUtils::getInstance()->setFileUrlReceived(urlStr);
    env->ReleaseStringUTFChars(url, urlStr);
    return;
}
JNIEXPORT void JNICALL
  Java_org_ekkescorner_examples_sharex_QShareActivity_setFileReceivedAndSaved(JNIEnv *env,
                                        jobject obj,
                                        jstring url)
{
    const char *urlStr = env->GetStringUTFChars(url, NULL);
    Q_UNUSED (obj)
    AndroidShareUtils::getInstance()->setFileReceivedAndSaved(urlStr);
    env->ReleaseStringUTFChars(url, urlStr);
    return;
}
#ifdef __cplusplus
}
#endif

If you want to learn more about JNICALL and other ways to manage them, here‘s a great Blog Post from BogDan Vatra : qt-android-episode-5.

JNICALL is like a bridge from Java to C++:

void AndroidShareUtils::setFileUrlReceived(const QString &url)
{
    QString myUrl;
    if(url.startsWith("file://")) {
        myUrl= url.right(url.length()-7);
    } else {
        myUrl= url;
    }
    // check if File exists
    QFileInfo fileInfo = QFileInfo(myUrl);
    if(fileInfo.exists()) {
        emit fileUrlReceived(myUrl);
    } else {
        emit shareError(0, tr("File does not exist: %1").arg(myUrl));
    }
}

This JNICALL enables us to emit the SIGNAL that we received an Intent from another Android App providing access to a File.

02_process_intent_from_other_app

Test it !

Now it‘s a good point to test it. Open Google Photos, select an Image and ShareWith shows ‚ekkes SHARE Example‘ App as target:

03_see_our_app_as_target

Select ‚ekkes SHARE Example‘ and Android will open our App. If the App already was opened, the current Page will be displayed and include the Image:

04_our_app_received_image_from_other_app

onActivityResult() (JAVA) vs QAndroidActivityResultReceiver (JNI)

While writing Part 1 of this Blog series I didn‘t found a way to get the Result back if Intent was started with Result from QShareUtils.java (Workflow ‚A‘ – the JAVA way)

QtNative.activity().startActivityForResult(Intent.createChooser(viewIntent, title), requestId);

@hamalaiv comment pointed me to the right direction: to get the Result back a Custom Activity must be used.

Ok – we‘re now using a custom Activity (QShareActivity.java) – let‘s test it.

To get the Result back, we must be implemented this JAVA code:

public class QShareActivity extends QtActivity
{
    public static native void fireActivityResult(int requestCode, int resultCode);

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        fireActivityResult(requestCode, resultCode);
    }
}

and this C / C++ Code:

JNIEXPORT void JNICALL
  Java_org_ekkescorner_examples_sharex_QShareActivity_fireActivityResult(JNIEnv *env,
                                        jobject obj,
                                        jint requestCode,
                                        jint resultCode)
{
    Q_UNUSED (obj)
    Q_UNUSED (env)
    AndroidShareUtils::getInstance()->onActivityResult(requestCode, resultCode);
    return;
}
void AndroidShareUtils::onActivityResult(int requestCode, int resultCode)
{
    processActivityResult(requestCode, resultCode);
}
void AndroidShareUtils::processActivityResult(int requestCode, int resultCode)
{
    if(resultCode == RESULT_OK) {
        emit shareEditDone(requestCode);
    } else if(resultCode == RESULT_CANCELED) {
                if(mIsEditMode) {
                        // check Timestamp and
                        emit shareEditDone(requestCode);
                        // or
                        emit shareFinished(requestCode);
                        return;
        }
        emit shareFinished(requestCode);
    } else {
        emit shareError(requestCode, tr("Share: an Error occured"));
    }
}

05_overview_share_files_with_other_apps_v2

Testing the JAVA way sharing Files with other Android Apps now works as expected with or without ResultCode. I removed the workaround code from ApplicationUI.

But there‘s a Drawback: Testing the JNI way (Part 1, Workflow ‚B‘) using QAndroidActivityResultReceiver only works without ResultCode. If starting the Intent with ResultCode

QtAndroid::startActivity(jniIntent, requestId, this);

the QAndroidActivityResultReceiver wasn‘t called for the Result: instead the Result comes back via our custom Activity onActivityResult() method using a wrong RequestId.

Lesson learned: never use QAndroidActivityResultReceiver (C++, JNI) and onActivityResult() (JAVA Activity) together in one App.

To test this please comment or rename onActivityResult() and you‘ll see the JNI implementation alone works well.

There‘s another problem if we want to share one of our Files with other Apps, because now we‘re source and also target for same MimeTypes. This means: our own App will be listed as a target:

06_own_app_is_target

Tried some ways to workaround, but doesn‘t work well and selecting the own App as Target with Result crashed the App because there was a collision with LaunchMode ‚singleInstance‘.

Unfortunately there‘s no way to exclude targets using Intent Filter. So we have to create a custom Chooser without listing our own App as Target. Thanks to this thread at StackOverFlow I found a way to solve this. Please take a look at QShareUtils.java

public static boolean createCustomChooserAndStartActivity(Intent theIntent, String title, int requestId) {
        // ...
}

Here‘s a short summary:

  • Create Intent as before and use as template
  • Context is QtNative.activity()
  • Get packageManager from context.getPackageManager()
  • Retrieve all Apps (List) for Template – Intent: packageManager.queryIntentActivities()
  • Sort Apps (List) by Label
  • From Apps (List) create List
    • Create Intent as Copy from Template – Intent
    • Add setPackage(targetPackageName)
    • Don‘t add Intent to list if Target PackageName equals own PackageName
    • If needed also watch a Blacklist

We created a List of Intents, where all Intents are based on our previously created Template – Intent with an extra PackageName added. This means each Intent will only detect one specific App as Target.

Now the tricky part:

We want to all collected Intents as EXTRA_INITIAL_INTENTS and we‘re using the last one from list of Intents as initializing Intent because the Chooser adds its initializing Intent to the end of EXTRA_INITIAL_INTENTS 🙂

Intent chooserIntent = Intent.createChooser(targetedIntents.remove(targetedIntents.size() - 1), title);
if (targetedIntents.isEmpty()) {
    Log.d("ekkescorner", title+" only one Intent left for Chooser");
} else {
    chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetedIntents.toArray(new Parcelable[] {}));
}

QtNative.activity().startActivityForResult(chooserIntent, requestId);

07_own_app_excluded_from_targets

Now it works as expected: our own App doesn‘t appear in list of target Apps.

Attention: onActivityResult() (JAVA) sometimes is too fast compared with QAndroidActivityResultReceiver (JNI)

While testing Action SEND I noticed that Files are not copied to other Apps or not printed, because ‚File does not exist‘. This happens only if going the JAVA way getting the Result back from onActivityResult(), but not if using JNI and QAndroidActivityResultReceiver.
When getting the Result back I‘m deleting the File from Documents Folder (see Part1) and it seems that onActivityResult() sends the Result before File was fully copied / printed.
As workaround I added a Timer with 500ms delay in QML before deleting the File.

From now on I‘ll use the JAVA way (Workflow ‚A‘ in Overview) as default to share Files with other Apps, because it‘s much easier to implement specific behaviour (as our Chooser Intent) using JAVA instead of JNI. The pure JNI way still remains in the code to get a feeling HowTo solve it without JAVA code.

Share from other iOS Apps with our Qt App

As next I want to implement the same functionality for iOS – so stay tuned for Part 3.

My goal is always to provide easy-to-use x-platform solutions for Android and iOS. That‘s why I‘m using Qt 😉

Some years ago I developed mobile business Apps for BlackBerry10 (BB10) where the InvocationFramework makes it easy to share content or files between Apps.

BB10 Customers are now (and next years) in transition from BB10 to Android, iOS or both and of course I want to motivate them to use Qt as their new Framework. Last two years I implemented most of my patterns using Qt and QtQuickControls2 for Android and iOS. Having features only for one platform is like having no solution. Step-by-step I‘m reaching feature-parity with BB10.

I‘m also blogging about all of this to make it easy for mobile devs to start with Qt.

Back to Sharing Example.

Need Help – offer Bonus

As I understand it right, iOS Extensions should be used to share Files or Content from other iOS Apps to our Qt App and it seems that this isn‘t supported yet our of the box from QtCreator. If there are some manual steps needed I need a description HowTo do this.

Remember: I‘m a mobile Business App Developer, not a Xcode expert and not an Obj-C expert and I‘m doing all my work from QtCreator – not commandline.

I‘m open for all tips and any help HowTo …

  • Open Qt App from other iOS Apps sharing Text Content or Files
  • Reopen Qt App from other iOS Apps at current Page if Qt App is already running
  • Emit SIGNAL about received Files or Text and copy Files into AppData location
  • Development workflow to solve this from QtCreator / Xcode

Bonus: If someone can provide a small sample project before I found out all the steps and blogged about, the first one will get a Bonus of 500 € from me. Please be aware that I‘ll modify and include code into my Share Example as Open Source code.

Have Fun

Now it‘s time to download current Version from Github, build and run the Sharing Example App.

The post Sharing Files on Android or iOS from or with your Qt App – Part 2 appeared first on Qt Blog.

Want to give Qt OpcUa a try?

basysKom has initiated Qt OpcUa which is a module offering support for the industrial communication standard OPC UA. This module has been contributed by us to the Qt Project. The Qt OpcUa API wraps existing OPC UA implementations. Currently, implementations for freeopcua and open62541 are available. Qt OpcUa will become part of the Qt 5.11 release as a Technology Preview. First alpha builds will be available by … Continue reading Want to give Qt OpcUa a try?

Protecting a Qt Application or a Device Against Hacking, Part 1

Open-source applications are open by nature, indented and encouraged for tweaking, hacking and further development. For a commercial, business critical, application or a device there often is a desire to make it closed and prevent modifications. Protecting against reverse engineering and tampering is important especially when the application or device processes valuable content, licenses, IPR or monetary transactions. Because of the dual licensing, Qt offers a commercial license option that is well suited for making closed applications and devices allowing many ways of protection. Everything can be hacked, but it is possible to make hacking your Qt based application or device difficult.

For the purposes of this blog post, I have divided the ways to protect your application into three categories: securing the application, external protection and legal aspects of protecting against hacking. I have blogged about this topic three years ago, but as the topic is still very relevant, I decided to re-visit it.

In this post, I concentrate on a few easily achievable and simple ways that everyone can do to make the reverse engineering of a Qt based application more difficult. Protect your Qt code form hackingIf there are specific reverse engineering or tampering related risks in your system, for example related to processing licenses, valuable content or media, proprietary algorithms or financial transactions, then you may want to consider additional security controls for tamper resistance. Some of these more advanced anti-hacking techniques are discussed in the upcoming part 2 or this blog post series (planned for February).

Securing the application

A commercial Qt license makes it legal to prevent modifications and reverse engineering, therefore it is also possible to take actions to prevent these. Static linking is one of the easiest ways of protection as it prevents simple swapping the Qt binaries used by the application and thus makes reverse engineering harder. With static linking the application and Qt libraries are combined into a single binary. A possible hacker needs to take much more demanding and time-consuming ways to modify the behavior of the system compared to dynamically linked application.

When using static linking a good further measure to protect against reverse engineering is to strip symbol names away from the binary. Depending upon the compiler you use this is either automatically done for release builds, or can be done as an extra step for the statically linked binary. For example, MSVC automatically removes symbol names from a statically linked Windows release binary. On Linux and Mac this can be done with strip command. After the symbol table is removed from the statically linked binary, it is quite well protected against casual reverse engineering. These steps are easily done and do not require modifying your Qt application source code, thus static linking and removing the symbols can be regarded as the easiest way to make reverse engineering a lot harder.

If use of dynamic linking is needed, some protection against simply changing the dynamically linked Qt library to a new version can be gained via using libinfix to change the library names (e.g. change libQt5Core to stgElse). Another approach is to change the used namespace with configure option –qtnamespace. Namespacing Qt puts all classes inside a defined namespace resulting, for example, QString being called someNewName::QString. The protection gained by using these means to a dynamically linked library is much weaker than using static linking, and should not be used if it is possible the link statically.

It is also possible to use a generic C++ obfuscator for Qt applications. These programs can both make reverse engineering harder and provide tamper protection by preventing a modified binary from operating properly. Unlike static linking, obfuscation and tamper protection cause some overhead, requiring additional CPU cycles to run the application (typically the more the code is protected, the higher is the additional load). When considering the right level of obfuscation, it should be noted that no matter how much the code is obfuscated, it is still eventually possible to reverse engineer.

For Qt Quick applications it is beneficial to use Qt Quick Compiler if it is desired to protect the QML parts of the application. Or use QML caching, which offers similar protection (both are available with Qt 5.9 LTS). While the application logic, and thus typically the most critical parts to protect, is usually done with C++ and therefore always compiled to binary, it is a good further measure to also make it harder to reverse engineer the QML parts of the application. The Qt Quick Compiler compiles QML to binary and protects against reading the QML parts of the application. An additional benefit of the Qt Quick Compiler is improved performance of the application, especially at startup.

External protection

As the Qt application is just like any other binary running in your target operating system and hardware, you can use all generic means available for protection in your environment. Operating system may offer means to digitally sign the applications, as is typically the case with the applications distributed via application stores. There are also many different products available to provide security for application binary, and these typically work for Qt applications as well.

For embedded devices, the nature of the environment provides some protection, as it is often difficult to get access to and modify software embedded into a physical device. Securing the boot sequence and protecting the integrity of any software updates will make the work of an attacker harder. The most efficient protection is to have hardware containing functionality to prevent a modified binary from running (either in the device hardware, or an external one, such as a USB dongle). In some cases, it is also possible to leverage another system via a network connection to provide tamper protection. Optimally, the system should deploy multiple overlapping security controls according to the defense in depth design principle, starting from static linking and obfuscating the binary to make reverse engineering difficult.

Legal aspects of protection

Whereas the applications and devices using the open-source licensed Qt should be open for hacking and playing around, it is possible to prevent such when using a commercial license of Qt. One of the most important legal protection means provided by the commercial license of Qt is to forbid modifications and reverse engineering – and allowing the use of various technical countermeasures.

Commercial license of Qt makes it possible to forbid reverse engineering, which is very often desired in the end user license agreement of an application or device. However, for an application or a device created with the open-source licensed Qt it is not allowed to prevent reverse engineering or modifications, neither in the end user license agreement nor by making it difficult.

It should be noted that also leveraging external protection typically requires a commercial Qt license, similarly as the other means of preventing user from changing or modifying Qt libraries. In the LGPLv3 license it is clearly stated in the license that in addition to being allowed to modify the open-source library the user must be able to run the modified library as part of the system, and that the needed tools to achieve this must to be provided.

Summary

If there is a need to create a closed application or a device, the commercial license of Qt is the right choice. With the commercial license, it is possible to utilize various means for making it difficult to modify functionality of the application or device built with Qt:

  • Making reverse engineering forbidden in end user license agreement and taking actions to prevent reverse engineering is allowed when Qt is used with a commercial license.
  • An easy way to protect against modifications and reverse engineering is to use static linking instead of dynamic linking and build Qt into same binary with your application to prevent changing the Qt libraries.
  • When using static linking it is important to use release builds and when needed to manually strip out the symbols before distributing the binary.
  • If dynamic linking is needed, some protection against reverse engineering can be gained by building Qt with different names for libraries using libinfix and changing the used namespace. This mainly protects against user being easily able to change the dynamically linked Qt libraries – the protection against reverse engineering provided by static linking is much better.
  • Using Qt Quick Compiler that compiles QML to binary and protects against reading the Qt Quick parts of the application is an efficient way of further protecting your application.
  • If desired, use 3rd party obfuscators or tamper prevention to make reverse engineering even harder – most of those work well with Qt applications protecting the C++ parts of the binary.
  • Leverage the capabilities of the hardware and the operating system to prevent modifications to system software.
  • If your Qt based system works in a risk environment where anti-tampering is particularly important, then stay tuned for the second part of this blog post series (planned for February).

In case you want to learn more about the ways to protect your application or a device, feel free to contact us. We can help you to implement protection for your application both via technical and licensing means. If you need advice, our consultancy services will help you find the right approach for protecting your Qt application or device.

The post Protecting a Qt Application or a Device Against Hacking, Part 1 appeared first on Qt Blog.

Qt 3D Studio Remote Deployment on Android Devices

This blog post will get you started with Qt 3D Studio remote deployment to Qt 3D Viewer on Android devices. This feature enables making changes to your Qt 3D Studio presentation on your computer and seeing the changes live on the target device.

Qt 3D Studio Editor & Viewer with the remote connection feature

Qt 3D Studio Editor & Viewer with the remote connection feature

Setting Up the Target Device

To set up the target device for remote deployment, follow the steps below.

  1. Download Qt 3D Viewer from Google Play and install it on the target device.
  2. Run the Viewer.
  3. From the file menu, select File > Connect.
  4. Enter desired port or use default port, press OK.
  5. Connection information, IP address and port, will be displayed in the Viewer.

Connecting to the Target Device

To connect to the target device from Qt 3D Studio, follow the steps below.

  1. From the file menu, select File > Connect to Device.
  2. Enter the connection information displaying in the Viewer on the target device, then press OK.
  3. You are now connected. The Viewer on the target device should display Remote Connected.

Preview Presentation

To preview the presentation on the target device, press the Preview button in the Studio toolbar. After making changes to your presentation, press the Preview button again to see the changes on the target device.

studio-preview-button

The Preview button in Studio

Disconnecting from the Target Device

You can disconnect from the target device either by selecting File > Connect to Device from the Studio file menu, or by selecting File > Connect from the Viewer file menu.

The post Qt 3D Studio Remote Deployment on Android Devices appeared first on Qt Blog.

New in Qt 5.10: QThread::create

If you are using Qt and you need to run some code in a separate thread, chances are that you are using QThread for the job. QThread is a very old class in Qt, making its first appearance in Qt 2.2, released on the 22nd of September 2000. Its responsibility is to start a new thread, and let you execute code in that thread.

There are two main ways of running code in a separate thread using QThread:

  1. subclassing QThread and overriding run();
  2. creating a "worker object" (some QObject subclass) and connecting it to QThread signals.

Please refer to my Qt World Summit 2017 presentation (video) for more details about how to use these two functions. There are also lots of other documentation and blog posts available on the internet, discussing the pros and the cons of the two approaches.

Enter QThread::create()

In Qt 5.10 I have added another way to run some code in a new thread. Inspired by C++11's std::thread, I have introduced QThread::create, a factory function that creates a new QThread which will run a user-provided callable:

[sourcecode lang="cpp"]
QThread *thread = QThread::create([]{ runSlowCode(); });
thread->start();
[/sourcecode]

The advantage of this approach is that it avoids creating a new QThread subclass manually for the sole purpose to override its run() member function and run some code.

Unlike std::thread, however, the newly-created thread is not automatically launched; the user is expected to start it with an explicit call to start(). This allows users to do some extra work before starting the thread, for instance, connect to its signals, set the name of the thread, or change its priority, as demonstrated by this snippet:

[sourcecode lang="cpp"]
QThread *thread = QThread::create(myFunction);

thread->setObjectName("WorkerThread"); // name to appear in ps, task manager, etc.
connect(thread, &QThread::started, gui, &Gui::threadHasStarted);

thread->start();
[/sourcecode]

The user acquires ownership of the newly-created QThread object.

With a C++17 capable compiler it is also possible to pass extra arguments to QThread::create(): these arguments will be then passed to the function in the new thread, using the same semantics as std::thread's constructor. For instance:

[sourcecode lang="cpp"]
QThread *thread = QThread::create(myFunction, arg1, arg2);
// extra setup...
thread->start(); // calls myFunction in another thread, passing arg1 and arg2
[/sourcecode]

That's it! I hope this will be useful to you. However, the API of QThread::create() is only half of the story. If you want to know more about what this apparently little patch meant for Qt, keep reading.

Implementation notes

In afterthought, the implementation of QThread::create is very simple. However, when doing the necessary research, I found a small oddity with the C++ Standard Library that deserves some attention.

The most important constraint I imposed on myself when writing QThread::create was to be as compatible as possible with std::thread's constructor semantics. If one reads the relevant section in the Standard (f.i. here), one can see that the actual semantics of the invocation are quite complex. The extra arguments to the function undergo a transformation (specified as DECAY_COPY), they're stored "somewhere", and finally the functor is invoked using the INVOKE function. Both DECAY_COPY and INVOKE are not actual Standard Library functions, but pseudo-functions formally defined elsewhere in the C++ Standard.

An oddity that I found during my research is that the C++11 Standard does not provide these building blocks to end-users (and by that I also include developers of other C++ libraries, like Qt). A way to use INVOKE for user code got added in C++17 with the std::invoke function; however, there is simply nothing ready-made that applies the DECAY_COPY transformation, and stores the callable and its decayed/copied arguments, and possibly even invokes the function with the right semantics.

The only workaround that I have found was to use a higher-level thread primitive provided by the Standard Library: std::async. std::async has the same invocation semantics as std::thread's constructor; it returns a std::future that we can use to examine the result.

We can force std::async to not spawn an additional thread and to run the callable only when asked to do so, by specifying the std::launch::deferred launch policy. std::launch::deferred allows a sort of lazy evaluation: the callable is invoked only when one tries to access the result (via the returned std::future object):

[sourcecode lang="cpp"]
auto future = std::async(std::launch::deferred, function, arg1, arg2);

// some time later, possibly in another thread:
future.get(); // calls the function with the arguments
[/sourcecode]

This is exactly how QThread::create() is implemented: it calls std::async on the user-provided function and its arguments, and stores the returned std::future in an instance of an internal QThread subclass. This subclass overrides run(), which in turn simply calls get() on the future (see here and here). This machinery isn't zero-overhead, as the combination of std::async and std::future does way more than what we need, but it at least shifts the burden of the implementation from Qt onto the Standard Library.

The extra plumbing needed is the aforementioned std::invoke, which however is only available in C++17. Under a C++17 compiler, QThread::create() takes a callable and its arguments, whereas under a pre-C++17 compiler, QThread::create() only supports a callable (and no further arguments). In practice this isn't a big limitation, because one can always use a lambda to wrap the callable and its arguments, and then pass the lambda as the only argument.

Qt and the Standard Library ABI

The addition of QThread::create() to Qt was also possible because of a major policy change in Qt.

A little bit of history: up to and including version 4, Qt did not require the presence of a Standard Library implementation on the system (the configure script had a -no-stl option). From version 5.0 Qt started to require a working Standard Library implementation: the configure switch was dropped, and Qt code dealing with Standard Library types was compiled unconditionally. Qt itself started using Standard Library types (notably, containers) in its own implementation, mostly where such types provided more features or performance when compared to Qt's own versions.

However, Qt has never exposed Standard Library types from its public ABI: all functions taking or returning Standard Library types had to be inline (example). The main reason for doing so was to make the same build of Qt compatible across different Standard Library implementations (e.g. libc++ and stdlibc++, or two ABI-incompatible builds of the same implementation).

After lots of debate, this has finally changed in Qt 5.10: Qt is now allowed to expose Standard Library types from its public ABI. Users wishing to mix-and-match Standard Library implementations need to recompile Qt. (In other words, we've made this Someone Else's Problem).

What does this have to do with QThread::create()? As I explained before, the current implementation uses std::async under the hood. The std::future returned by std::async is then passed to an exported, out-of-line Qt function, which constructs and returns the result -- the QThread instance that runs the user-supplied function. (The reason for doing such a thing is because QThread is an exported polymorphic class. A subclass defined entirely inline would generate copies of its vtable / type_info in all the translation units using it, and this may cause problems.) Therefore, now std::future is part of Qt's own ABI; and I'm very glad I didn't have to re-implement it! continue reading

The post New in Qt 5.10: QThread::create appeared first on KDAB.

Qt Webinars in January!

Hi and Happy New Year everyone!

Let’s start it off with a blast in the shape of some veritable webinar doozies. This January, we want to talk to you about Qt 5.10 in all its glory, give you an introduction to our awesome Qt 3D Studio and keep you safe with Qt Safe Renderer.

Don’t have the time to participate live? Sign up anyway – we will send you a recording and the presentation slides to your email address!

9 January: Meet Qt 5.10 Asia and EU time zone – 11 January: US and EU time zone

Overview of the new features in the latest release of the Qt framework and tools. Qt 5.10 is a quality upgrade spiced with several new features on top of the previous release,  Qt 5.9 LTS. New features in Qt 5.10 include a mechanism to use graphical assets in UI design created and maintained in a 3rd party tool, Vulkan support, improved gesture enablers, WebGL based remote applications, solutions for M2M communications, Qt Creator 4.5 and much more.

16 January: Introduction to 3D Studio EU and Asia time zones – 18 January: US and EU time zone

In this webinar, we will go through the Qt 3D Studio key features, show how you can create interactive 3D user interfaces with Qt 3D Studio and answer your questions live.

25  January: Functional Safety with Qt and Qt Safe Renderer

In this webinar, we talk about functional safety and how the Qt Safe Renderer can be used to create functionally safe systems.

Missed a webinar such as our Qt Graphics AMA session or the one on the controversial million-dollar-question “Qt or HTML5”? No problem! You can find recordings of all our webinars in the on-demand section! Download them and get smart!

The post Qt Webinars in January! appeared first on Qt Blog.

Qt 5.10 Windows Rendering Benchmarks

At the end of the previous blog post I promised to do a follow-up about Windows side rendering performance. So here we go.

But before that, let's revisit one earlier case. Previously decided to provide results with and without "Bezier lines" test because it seemed to perform particularly poorly when rendered using QML Shape backend. Instead of just letting this one go, I decided to dig a bit deeper to try to improve QML Shape performance. After all, just disabling slow tests doesn't sound like a preferred long-term plan... ;-)

Improving QML Shape paths performance

After some trial and error, found out that QQuickPath::createPath() uses considerable amount of time every time path changes and reason is in QPainterPath::length() which e.g. for all curved paths calls QBezier::length(). This isn't usually problem for PathView as its normal use-case is creating path once and then just moving elements along the path. But new ShapePath on the other hand might be animated and change (re-create) its path multiple times.

Luckily ShapePath wouldn't actually need to count its length there, as it doesn't support PathAttribute or PathPercent properties. So I went ahead and implemented a fast-path for creating path for ShapePath in patch linked into QTBUG-64951. This patch improves performance of all QML Shape paths, most notably for bigger animated paths. For QNanoPainter demo bezier line test, it provided up to ~20x performance boost on Windows PC. See FPS values at top-left corners of without (back windows) and with the patch (forward windows):


Hopefully that patch ends up into Qt in one form or another, but for now to get better results I used Qt 5.10 branch + patch for all the testing here.

Desktop OpenGL vs. Angle

As you probably know, Qt Quick on Windows PC can run either on desktop OpenGL or on OpenGL ES through Angle. What Angle does, is translating OpenGL ES API calls for Direct3D which is great to improve compatibility as OpenGL support varies on Windows. Direct3D drivers of different GPUs might be more optimized than OpenGL but on the other hand translation has some overhead so one might ponder which one is faster, normal OpenGL or Angle? How much does it matter?

To get some view into this, I ran the test application first on trusty old Windows PC with the following related specs:
  • Intel i5-2500K @ 3.3GHz
  • Integrated Intel HD Graphics 3000 + NVIDIA GTX 1060
  • HD monitor
  • Windows 10
  • Building with MS Visual Studio 2015 - 64bit
  • All QNanoPainter perf demo default tests enabled

Test1: Windows PC with HD Graphics 3000, fullscreen:


Test1 conclusions: No huge differences in performances. Likely old integrated Intel GPU is so non-performant that none of the rendering methods can perform well. With QQuickPaintedItem (QImage and FBO), Angle is faster than desktop OpenGL. With all the other backends, OpenGL is faster. But as said, differences aren't very big.

Full HD resolution is probably too much for that integrated GPU to handle, so let's repeat the test with default window size (375x667).

Test2: Windows PC with HD Graphics 3000, default window size:


Test2 conclusions: Interesting part here is naturally comparison with the Test1 results. Immediately we can see QNanoPainter (OpenGL) outperforming other options. Decreasing the item size improved its fps ~200%, while QNanoPainter (Angle) only gained ~40%. So decreasing the pixel amount allows OpenGL to perform better than Angle. In many tests QQuickPaintedItem (QImage) has been the slowest option, but here it's actually second fastest. Combination of fast Intel CPU + poor GPU + small item size suits it well. Not really surprising, but good to prove it.

It is also interesting that item size didn't have big affect for QML Shape nor QQuickPaintedItem(FBO), both of those are limited by something else than item pixel amount. But some results are a bit shady, leading to think that maybe this older integrated GPU is doing strange things.

Next we can enable the external NVIDIA GTX 1060 graphics card and see how huge GPU performance increase affects these different rendering methods. As QML Shape supports NVIDIA-specific GL_NV_path_rendering we need to add into our test matrix one more option, with and without vendorExtensionsEnabled property enabled. To keep things clearer, let's do Angle and OpenGL as separate graphs from now on.

Test3: Windows PC with NVIDIA GTX 1060, Angle:


Test4: Windows PC with NVIDIA GTX 1060, OpenGL:


Tests 3 and 4 conclusions:
  • No difference for QQuickPaintedItem (QImage) between Test 3 & 4. This is as expected, Qt Raster CPU backend performs equally well (or bad) with OpenGL and Angle.
  • With QML Shape (no GL_NV_path_rendering), OpenGL and Angle perform quite close to each other, with OpenGL being ~20% faster.
  • Angle doesn't have GL_NV_path_rendering extension available so on Angle results with and without vendorExtensionsEnabled are exactly same. On OpenGL, GL_NV_path_rendering gives about 20% performance improve over default GeometryRenderer.
  • QQuickPaintedItem (FBO) is clearly faster with OpenGL, about double the speed compared to Angle. QNanoPainter is also much faster with OpenGL, about 4x the speed compared to Angle.
As we are in a good benchmarking flow now let's not stop yet. Next we will switch to fresh laptop hardware, Dell XPS 15 (9560) with the following related specs:
  • Core i7-7700HQ
  • Integrated Intel HD 630 + NVIDIA GTX 1050
  • HD screen
  • Windows 10
  • Building with MS Visual Studio 2015 - 64bit
  • All QNanoPainter perf demo default tests enabled
So how does a laptop with latest Intel CPU + GPU perform? What's the difference between integrated vs. additional GPU here? Let's find out, running all test in fullscreen HD resolution.

Test 5: Dell XPS 15 with Intel HD 630, Angle:


Test 6: Dell XPS 15 with Intel HD 630, OpenGL:


Test 7: Dell XPS 15 with NVIDIA GTX 1050, Angle:


Test 8: Dell XPS 15 with NVIDIA GTX 1050, OpenGL:



Tests 5-8 conclusions:
  • With HD 630, OpenGL is also overall faster than Angle. With QML Shape and QQuickPaintedItem (FBO) OpenGL reaches ~100% higher fps, while with QNanoPainter OpenGL has ~50% higher fps than Angle.
  • Same thing with GTX 1050, OpenGL is faster than Angle. With QML Shape ~30%, QQuickPaintedItem (FBO) ~100% and QNanoPainter ~200% higher fps. It's clear that especially QNanoPainter enjoys taking all juices out of powerful GPUs.
  • Comparing integrated vs. external GPU here, enabling GTX 1050 increases QNanoPainter performance with ~200% (12fps vs. 34fps). Also interestingly QML Shape doesn't gain about anything from external GPU, so bottleneck is somewhere else. But enabling GL_NV_path_rendering for QML Shape with GTX 1050 gives ~20% higher fps which matches to results with other PC. So when running on NVIDIA GPU it's usually preferred to keep vendorExtensionsEnabled on.
  • As expected, Intel integrated GPUs have improved a lot in ~6 years. Looking at QNanoPainter numbers, this laptop with HD 630 performs ~4x faster than HD 3000 of previous setup (43fps vs. 11fps). Yes, other parts have changed too, but GPU is a big factor here.
  • Comparison between old system + NVIDIA GTX 1060 vs. new system + NVIDIA GTX 1050 is also interesting. We can see that although GPU is beefier, CPU, RAM etc. turn the table for the newer system. On new system, rendering is overall ~30% faster (QNanoPainter 34fps vs. 26fps).

Now it's probably good time to call this blog post done. As always, thoughts about these results, own testing results or any other comments are warmly welcome. And happy 2018!

Qt Cloud Messaging API Available for Embedded Systems

by Ari Salmi at SnowGrains (Qt Blog)

Challenges with cloud messaging for embedded devices has inspired the Kaltiot & SnowGrains teams to create a cross-platform Qt API which enables easy push messaging from and to embedded devices. The API is called the Qt Cloud Messaging API and it is built with flexibility and extensibility in mind.

We have decided to target other Qt areas, too, and make the API easily extensible to any service provider instead of being for embedded only. This enables developers to use the same API for both mobile and desktop development.

There is a vast number of push messaging providers for mobile and web development nowadays. Yet in industries like automation, automotive, robotics and for other embedded devices there has not really been any providers for this type of service. The need is increasing at a rapid pace as embedded and IoT devices are pushed more and more messages by the device owners, car service providers, telemetry, cloud and many others.

The Qt Cloud Messaging API is simple to adopt and take into use.

Let’s take a sneak peek at how to utilize the API for embedded systems and Android mobile platforms. We’ve integrated the Kaltiot Smart IoT SDK and the Firebase C++ SDK into the backend, and with the Qt Cloud Messaging API we can easily create e.g. a chat application on top of it.

Kaltiot Smart IoT is a service for scalable and secure messaging between devices and services. It provides bi-directional and always online, yet battery optimized communication. The client SDK is designed to work even in constrained low-end devices in poor wireless network conditions. Kaltiot Smart IOT is available for Linux32, Linux64, Raspberry Pi, Mac, Android.

How to use the Qt Cloud Messaging API for embedded devices:

 First, clone the qtcloudmessaging repository: git clone https://codereview.qt-project.org/qt/qtcloudmessaging

Service provider

Pre-requirements:

  1. Get the Kaltiot SDK to your embedded device from (e.g. Linux or Raspberry Pi SDK) https://console.torqhub.io
  2. Get the API key for sending channels or/and creating server-side implementation
  3. Add following line to your application .pro file: QT += cloudmessagingembeddedkaltiot
  4. Define KALTIOT_SDK path to your application.pro
  5. Add following includes to main.cpp
#include <QtCloudMessaging>
#include <QtCloudMessagingEmbeddedKaltiot>
  1. Add following QtCloudMessaging configs to main.cpp
// Instantiate CloudMessaging library
QCloudMessaging *pushServices = new QCloudMessaging();

// Add provider for Kaltiot Embedded systems
QCloudMessagingEmbeddedKaltiotProvider *kaltiotPushService = new QCloudMessagingEmbeddedKaltiotProvider()

// Provider based init parameters are given with QVariantMap
QVariantMap provider_params;

provider_params["API_KEY"] = "Your API key from the Kaltiot console for server communication";
// Creating name for provider which can be used cross your app.

pushServices->registerProvider("KaltiotService", kaltiotPushService, provider_params);

QVariantMap client_params;

client_params["address"] = "IOTSensor1";
client_params["version"] = "1.0";
client_params["customer_id"] = "Kaltiot";

// Creating default channels to listen
QVariantList channels;
channels.append("weather_broadcast_channel");
client_params["channels"] = channels;

// Connect IoT sensor client to system
pushServices->connectClient("KaltiotService", "IOTSensor1", client_params);

//! Automatically subcribe to listen one more channel e.g. WindInfo.
pushServices->subsribeToChannel("WindInfo", " KaltiotService ", " IOTSensor1");
  1. Provide context of cloud messaging to QML in main.cpp
//! Provide context to QML

engine.rootContext()->setContextProperty(“pushServices”, pushServices);

QML Part:

in main.qml catch the messages coming from the Kaltiot service.

  1. Receiving messages
Connections{
target : pushServices
onMessageReceived:{

//! Message is received as string and needs parsing to JSON
console.log(message)
}
}
  1. Sending messages:
//! Sending a broadcast message e.g. from the weather station:
//! Message structure for embedded devices is easy:
//! define payload json:
//! {
//!   “payload_type”:”STRING”,
//!   “payload”: encodeURI(JSON.stringify(payload))
//! }
//! Payload is your application specific. E.g:
var payload =
{
msgType:”NEW_WEATHER_INFO”,
city: “Oulu”,
forecast: “full sunlight for whole next week”
}

//! Capsulate payload to message and send it via QtCloudMessaging API:
var payload_array = [{“payload_type”:”STRING”,”payload”: encodeURI(JSON.stringify(payload))}]

pushServices.sendMessage(JSON.stringify(data), ”KaltiotService”, ”IOTSensor1”, ””, ”weather_broadcast_channel”);

Using the Qt Cloud Messaging API for Android / iOS mobile development with Google Firebase service provider

Pre-requirements:

  1. Create new project into Google Firebase console: https://firebase.google.com/
  2. Download Google Firebase C++ SDK  and add the Firebase configuration file for Android Gradle:
DISTFILES += \android/google-services.json
  1. Add following line to your application .pro file:
QT += cloudmessagingfirebase

Define Google firebase path into your application.pro file with

GOOGLE_FIREBASE_SDK = <Path_to_Firebase_SDK>
  1. Add following includes to your main.cpp
#include <QtCloudMessaging>
#include <QtCloudMessagingFirebase>
  1. Add following QtCloudMessaging setup to main.cpp:
// Instantiate CloudMessaging library
QCloudMessaging *pushServices = new QCloudMessaging();
QCloudMessagingFirebaseProvider *firebaseService = new QCloudMessagingFirebaseProvider();
QVariantMap provider_params;

// Server API key is not recommended to store inside to the application code due security reasons.
// But if you do, make sure it is inside compiled C file or if you are doing a server side implementation with C++ & Qt.
// SERVER_API_KEY Is needed to be able to send topic messages from the client without Firebase application server.

provider_params["SERVER_API_KEY"] = "Get your SERVER API KEY from the google firebase console";

// Registering the Google firebase service component.
pushServices->registerProvider("GoogleFireBase", firebaseService, provider_params);

/*! Connected client is needed for mobile device.
\param Service name "GoogleFireBase"
\param Client identifier name to be used inside the application
\param Parameters for the client. No params for firebase client.
*/
pushServices->connectClient("GoogleFireBase", "MobileClient", QVariantMap());

//! Automatically subscribe to listen one example topic
pushServices->subsribeToChannel("ChatRoom", "GoogleFireBase", "MobileClient");
  1. Provide context of cloud messaging to QML in main.cpp
//! Provide context to QML
engine.rootContext()->setContextProperty(“pushServices”, pushServices);

QML Part:

in main.qml catch the messages coming from the google firebase

  1. Receiving messages
Connections{
target : pushServices
onMessageReceived:{
//! Message is received as string and needs parsing to JSON
console.log(message)
}
}
  1. Sending messages:
//! For firebase, message structure needs to have data as whole message.
//! Notifications are shown in the Android/iOS notification center.
function sendMessage(notification_titile, notification_msg, msg){
var data = { “data”:{
“message”: {“text”:msg } },
“notification” : {
“body” : notification_msg,
“title” : notification_titile
}
}

//! Give data and service provider identifier as well as client name defined in the C++ code.
pushServices.sendMessage(JSON.stringify(data),”GoogleFireBase”,”MobileClient”,””,”ChatRoom”);
}

You can play around with the Qt Cloud Messaging API + firebase from the sample

https://github.com/snowgrains/qtcloudmessaging-examples

The Qt Cloud Messaging API provides cross-platform API to enable us to include new providers and still keep the development API as same.

The Qt Cloud Messaging API has been developed to be easily extensible to any service provider. Take a sneak peek at ongoing development from the https://codereview.qt-project.org/qt/qtcloudmessaging and contribute your own backend service to the community.

Kaltiot has been developing and hosting IoT cloud services since 2008. Trusted e.g. by Microsoft, Kaltiot passes through 10M messages every hour. (www.kaltiot.com)

SnowGrains provides Full Stack Software Solutions On The Rocks.(www.snowgrains.com)

Ari Salmi is multi-technology developer and Qt enthusiast.

The post Qt Cloud Messaging API Available for Embedded Systems appeared first on Qt Blog.

Push Messaging for embedded solutions

To fuel growth, Qt’s offering for developers is enhanced with a push notification service by Kaltiot. Developers can focus on their own embedded solutions and take full advantage of the ready-to-use smart connectivity and communication service.

Push messaging is widely used in mobile and web development. For instance, social media services use it to send notifications to mobile phones. Also, Push messaging is essential for industries like automation, automotive and robotics. The need for this functionality is increasing for embedded and IoT devices when more and more messages are pushed by backend service providers, cloud, and other services.

To help accelerate the growth within the automotive, healthcare and IoT sectors, to name a few, Kaltiot is widening its developer support with the Cloud Messaging API, which has been contributed to the Qt framework and is available now as source code through Git.

Developers can now focus on their own embedded solutions and utilize the ready-to-use through the API. The service provides bi-directional, and always online, yet battery optimized communication. By using the Cloud Messaging API, developers can easily create cross-platform applications with Qt and also take advantage of the Kaltiot solution for embedded, mobile and desktop development. The Kaltiot client SDK is designed for automated provisioning and works even in constrained low-end devices in poor wireless network conditions.


The players

Team Kaltiot has developed secure (push) and scalable messaging between devices and services since 2008. The cloud service is trusted by Microsoft among others and their social media partners who transmit notifications to tens of millions of users around the world. Kaltiot Smart IoT is currently available e.g. for Linux32, Linux64, RasberryPi, Mac, Android, and NodeJS. https://kaltiot.com

The post Push Messaging for embedded solutions appeared first on Qt Blog.

Cutelyst 1.12.0 released

The year is about to end and so is Cutelyst v1 series, I wasn't planning for another release this year but Matthias added some nice new features that I decided to roll 1.12 in 2017 branching 1.x.x series and master is now officially Cutelyst 2 with no stable API/ABI until 2.0.0 is tagged.

HTTP/2 support will hopefully be part of Cutelyst 2.0.0, there aren't any drastic changes in v2, most important thing is fixing MSVC builds and removing deprecated API.

Back to this release it includes a new CSRF protection plugin, with a Grantlee tag similar to what Django has. Add to this many fixes and the epoll event loop dispatcher is now even faster and got many fixes, performance wise it's great advantage is when dealing with many simultaneous connections, it can lead to 2-3 times faster than default glib one.

https://github.com/cutelyst/cutelyst/archive/v1.12.0.tar.gz

Happy new hacking year!

Aubergine – Playing with emoji

Playing with emojis

At some point, I needed to copy paste emojis, but couldn’t find a good way to do it. So what does a good hacker do?
Scratch an own itch. As I wrote about in the past, all these projects should be shared with the rest of the world.
So here it is: https://cgit.kde.org/scratch/sune/aubergine.git/

It looks like this with the symbola font for emojis: Screenshot

It basically lets you search for emojis by their description, and by clicking on a emoji, it gets inserted into the clipboard.

As such, I’m not sure the application is really interesting, but there might be two interesting bits in the source code:

  • A parser for the unicode data text files in /usr/share/unicode/NamesList.txt is placed in lib/parser.{h,cpp}
  • A class that tries to expose QClipboard as QML objects placed in app/clipboard.{h,cpp}. I’m not yet sure if this is the right approach for that, but it is the one that currently makes most sense in my mind. If I’m receiving proper feedback, I might be able to extend/finish it and submit it to Qt.

And of course, now it is simple to describe fancy cooking:

🍆 🔪 🔥
(aubergine) (hocho) (fire)

I ❣ emoji

An Unexpected C++ Journey

https://youtu.be/LVbhkgYba0s

Some of you may know that KDAB employees enjoy flexibility on working hours as well as location, and some choose to work from home, with the opportunity to share childcare, do part-time study or simply enjoy an out-of-the-way location. All that's required is a decent bandwidth for KDAB work.

About once a year, all of KDAB employees come together so we can get to know each other in a different way. Among other things, it allows new members of the KDAB family to introduce themselves via a ten-minute "newbie talk" on anything they are interested in.

The year Daniel Vrátil joined KDAB, he gave us this talk. We thought to show it again as a Christmas bonus to our readers. continue reading

The post An Unexpected C++ Journey appeared first on KDAB.

nanoQuill

 

Through their partnership, Qi and KDAB have developed imaging software to help researchers gain a deeper understanding of the progression of cancer on a cellular level, using Qt.

Qi, KDAB and Qt have now taken their collaboration one step further by introducing nanoQuill, otherwise known as “The Coloring Book of Life,” which is a crowdsourced coloring book and mobile app that gives anybody the opportunity to color cancer images to help annotate organelles inside those electron microscopy images. Qi is then able to take the crowdsourced annotations to measure a cell’s detail, render 3D images from the colored 2D images, and ultimately train new deep learning algorithms, all in the name of advancing cancer research.

With our user interface capabilities and KDAB’s unmatched technology expertise, Qi is able to advance its research without being impeded by technological restraints. Furthermore, with the nanoQuill coloring book project, Qi is able to leverage an entire global community to exponentially accelerate the world-changing contributions it’s making towards curing cancer.” —Michel Nederlof - CEO of Quantitative Imaging Systems

Just about everybody gets affected by cancer sooner or later, as a patient, a colleague, a relative, a caregiver, a neighbor, or a friend. Certainly, this applies to KDAB employees, too, which is why we partnered with Oregon Health & Science University (OHSU) and Quantitative Imaging (Qi) on several projects and we are very proud to be able to contribute, alongside with The Qt Company, to another great one — nanoQuill.

nanoQuill is a great opportunity for us to apply our world-class engineering skills in the area of Qt, 3D, OpenGL, C++, and platform-independent software development to an excellent cause. We are aware that many years of cancer research are still ahead of us before “cancer as we know it” can be considered a thing of the past, but with every colored image, we, you, and everybody who is contributing, advance that research a little bit.

Learn more and order your copy from nanoquill. continue reading

The post nanoQuill appeared first on KDAB.

Riding the curve

So, last time we spoke, I left you this teaser...

[video width="900" height="570" webm="https://www.kdab.com/wp-content/uploads/stories/next.webm" loop="true" mp4="https://www.kdab.com/wp-content/uploads/stories/next1.mp4" autoplay="true"][/video]

I mean how hard could it be? right?

OK So, hum, an easy enough exercise..... for a path-based animation like we saw last time.

But how do I draw such a path? How do I time the inflections in speed for that path, since time of occurrence is dependent on speed and not position?

Hmm, a roller-coaster seems like the ideal thing. I mean, it is a path and then the internal animation in that path depends on the path itself. In other words, the lower you are the faster you go.... right?

So let's start drawing, beginning with a plain svg path that I draw in my old Inkscape.... that is the basis of our roller coaster.

All good, right?  Next I need to move this to the 0,0 in Inkscape (not the page 0,0, but the real 0,0). Once the path is in place, copy the svg path code and insert it on the:


PathInterpolator {
id: pathInterpolator


path: Path {
PathSvg { path: "M 0,147.09358 C..........}..}

This works great! Make a car item bound to the progress property and... this is easy! But it's not very lifelike: it's just a car going at a constant speed across a a path.

Now the big question.... How do I make the animation believable?

I know, I will use my old friend spreadsheet and numerical math...
First of all, let's break this path into multiple segments so that I have an x,y coordinate system of sorts.

Inserting a console log that spews out the Y position with a timer did the trick for me.

OK, next we move this to a spreadsheet and say, for simplification, that the speed is Y.
All good, now we have a position in path vs speed graphic, we had an initial speed factor, and a final speed, so we can play with it.. and we get this:

 

But what we need is a time vs speed graphic.

Now let's calculate, at each position, its time in a cumulative fashion. We have that p=po+vt (we can say that v=(v0+v1)/2) (p=position). So because p0=p-1, we have:

p=p-1+(v0+v1)/2t<=>2/(v0+v1)=t

This produces our new x axis ....for the speed.

Next we just need to integrate the speeds over the time periods (position in the path at time) in order to create a curve shape that makes sense to easing.bezierCurve: 

 

Then we need to move this image (orange line) back into Inkscape in order to create a path out of it.

Almost there.... OK, remember that the direction on Inkscape is y down so we have to mirror this path down. Also, QML does not take real svg paths for this but a rather normalized curve that starts on 0,0 and ends on 1,1, and all nodes contain control points on both ends. To make things even simpler, they need to be described in absolute coordinates (Inkscape likes to save as relative). After some fiddling, I found out I can force Inkscape to use absolute coordinates when saving its xml... and I managed to put the path at point 0,0 to 1,1.

Now for the final step, just create a little script that transforms the svg string into a bezier array of points and we are good to go...... almost.

Add 2 more carts connected to the same base animation, make them all randomly bounce a bit, make them rotate while in the "fake" jump for fun and we are done.

Download the rolercoster.ods file if you for are interested.

Note: The jump phase implies that the path used for the car movements and the path used to acquire the speed are not exactly the same so a bit of testing and fiddling was needed.

Conclusion

It is not the easiest of processes to achieve such an animation, but it's flexible enough to solve a lot of similar problems where you know the speed you want at a given position, but don't know how to get there. It's useful if you want to simulate a physics-driven animation but you think adding a real physics engine to do it is overkill.

# The size of the bezier curve is limited for longer curves I would suggest breaking our curve into multiple ones.

The next blog will be about abusing the particle systems for profit, and fun... But that is something for next year.

  continue reading

The post Riding the curve appeared first on KDAB.

Qt Installer Framework 3.0.2 Released

We are happy to announce the release of Qt IFW 3.0.2.

3.0.2 is a bug fix release, here are some of the main fixes:

  • Add possibility to reopen admin query –  Previously in Windows you had 2 minutes to answer to the admin query if installer requested one. If query was not answered within the time frame, installer aborted. Now you have ability to reopen the admin query and continue install.
  • Implement package download resume – package downloads now recover after network failures from the same position, which helps installations of large packages over flaky networks. The download resume also works after switching a network connection or even after resuming a computer from suspended power-save state. This fix was contributed by Ville Voutilainen.
  • Do not reset core when pressing ‘Restart’ – This enables to restart the installer with the same command line arguments.

The full list of bug fixes can be found from ChangeLog.

Sources and precompiled binaries for the Installer Framework are available at download.qt.io (open source), or in your Qt Account. The binaries are built with Qt 5.9.0.

The post Qt Installer Framework 3.0.2 Released appeared first on Qt Blog.

How to Add Chat Service and Cross-Platform Leaderboard with User Profiles to Your iOS or Android App

A user chat, friend system or leaderboards increase app retention and engagement rates. This applies for games as well as business apps. V-Play Engine makes it easy to add such features with ready-made social services.

Boost Retention and Engagement Rates with Gamification Services

App retention and user engagement are important key drivers to build up your user base. Many games include features like leaderboards, friend systems or an in-app user chat. They help to keep players engaged and boost long-term retention. It is important to allow your users to connect, compete and form social relationships.

V-Play Engine offers a ready-made solution to help you do that. With the SocialView services, you can add Gamification features within minutes. You can for example:

  • Add User Accounts, Authentication and User Profiles
  • Show Leaderboards with User Highscores
  • Integrate an In-app Chat and Friend System
  • Store Custom User Data in the Cloud
  • and much more.

See this 10-minute video tutorial for a quick overview how it works:

How to Add a Leaderboard Cloud Service with User Profiles

Use a Cross-platform User Login Service for Authentication

It’s easy to integrate gamification features to your app. As everything starts with your users, the first step is clear. Your app has to provide user authentication and user account management.

To make this as easy as possible, the SocialView services work without registration. Every user that installs your app gets a unique user account linked to his device. The following example app takes care of the service configuration and shows the profile of the logged-in user:

import VPlay 2.0
import VPlayApps 1.0

App {

 // service configuration
 VPlayGameNetwork {
   id: gameNetwork
   gameId: 285
   secret: "AmazinglySecureGameSecret"
 }

 // social view setup
 SocialView {
   id: socialView
   gameNetworkItem: gameNetwork
 }
}

These few code lines are sufficient. Here is how the application looks like on iOS and Android:

SocialView User Profile

The default profile view supports changing the user name, country or profile picture. Furthermore, the profile is also extensible with a Facebook login. This allows to share the same synchronized user profile across different devices.

Show a Leaderboard in Your iOS or Android App

Apart from the user profile, there are many other SocialView pages you can use in your app. The following example extends the app with a leaderboard page. The user score of this demo matches the total number of app starts. It also adds a main navigation with two menu items. This means, that users can now switch between the profile and leaderboard page.

import VPlay 2.0
import VPlayApps 1.0
import QtQuick 2.9

App {

 // app navigation
 Navigation {
   NavigationItem {
     title: "User Profile"
     icon: IconType.user
     NavigationStack {
       initialPage: socialView.profilePage
     }
   }

   NavigationItem {
     title: "Leaderboard"
     icon: IconType.flagcheckered
     NavigationStack {
       initialPage: socialView.leaderboardPage
     }
   }
 }

 // service configuration
 VPlayGameNetwork {
   id: gameNetwork
   gameId: 285
   secret: "AmazinglySecureGameSecret"

   // increase leaderboard score by 1 for each app start
   Component.onCompleted: gameNetwork.reportRelativeScore(1)
 }

 // social view setup
 SocialView {
   id: socialView
   gameNetworkItem: gameNetwork
   visible: false // we show the view pages on our custom app navigation
 }
}

You get a native tab navigation on iOS, while Android users get a drawer menu. If this is not what you want, you can also change the default navigation mode for the platforms.

socialview-cross-platform-leaderboard

How to Integrate an In-App Chat Service and Store User Data in the Cloud

Add In-App Chat with Friend System and Push Notifications

Adding an in-app chat and friend system to your app is easy as well! With a few more configuration settings, the SocialView turns into a full-featured messenger.

SocialView Chat with Push Notification

This example replaces the leaderboard with a chat and adds the required configuration:

import VPlay 2.0
import VPlayApps 1.0

App {

 // app navigation
 Navigation {
   NavigationItem {
     title: "User Profile"
     icon: IconType.user
     NavigationStack {
       initialPage: socialView.profilePage
     }
   }

   NavigationItem {
     title: "Chat"
     icon: IconType.comment
     NavigationStack {
       initialPage: socialView.inboxPage
     }
   }
 }

 // service configuration
 VPlayGameNetwork {
   id: gameNetwork
   gameId: 285
   secret: "AmazinglySecureGameSecret"
   multiplayerItem: multiplayer
 }

 VPlayMultiplayer {
   id: multiplayer
   appKey: "dd7f1761-038c-4722-9f94-812d798cecfb"
   pushKey: "a4780578-5aad-4590-acbe-057c232913b5"
   gameNetworkItem: gameNetwork
 }

 // social view setup
 SocialView {
   id: socialView
   gameNetworkItem: gameNetwork
   multiplayerItem: multiplayer
   visible: false // we show the view pages on our custom app navigation
 }
}

When you open the chat, you first see the inbox page with all ongoing conversations. From this view you can start new chats or write with existing contacts. The chat service also allows to configure support for push notifications. Users are then notified whenever a new message comes in.

SocialView Chat Inbox and Friend System

As soon as you add the chat to your app, your users can also become friends. This is possible by sending friend requests using the chat system. The SocialView is your single access point for all services. It also shows your friends in a separate section of your leaderboard.

Use Cloud Storage to Store and Search Custom User Details

Another cool feature of the SocialView is the integrated cloud storage: You can enrich your user accounts with custom data for your project. For example, you can let users enter their favorite music genre or food:

SocialView User-based Cloud Storage

The following snippet extends the user profile with custom UI elements to enter and view this data:

 // …

 SocialView {
   id: socialView
   gameNetworkItem: gameNetwork
   multiplayerItem: multiplayer
   visible: false // we show the view pages on our custom app navigation

   // extend user profile with fields to allow storing additional user details
   profileUserDelegate: SocialUserDelegate {
     height: otherUserCol.visible ? otherUserCol.height : userDetailCol.height

     // parse the JSON data stored in customData property, if it is set
     property var userCustomData: !!gameNetworkUser && !!gameNetworkUser.customData ? JSON.parse(gameNetworkUser.customData) : {}

     // column visible for logged-in user, allows editing the custom fields
     Column {
       id: userDetailCol
       x: dp(Theme.navigationBar.defaultBarItemPadding) // add indent
       width: parent.width - 2 * x
       spacing: x
       // only show if profile of logged-in user
       visible: gameNetworkUser.userId === gameNetworkItem.user.userId 

       AppText {
         text: "Edit the fields below to set your details."
       }

       // custom data fields
       Column {
         spacing: parent.spacing

         Row {
           spacing: parent.spacing
           Icon {
             id: inputIcon
             icon: IconType.music; color: Theme.tintColor
             anchors.verticalCenter: parent.verticalCenter
           }
           AppTextField {
             id: songInput
             text: !!userCustomData.song ? userCustomData.song : ""
             width: userDetailCol.width - parent.spacing - inputIcon.width
             placeholderText: "Enter your favorite music genre."
             borderWidth: px(1)
           }
         }

         Row {
           spacing: parent.spacing
           Icon {
             icon: IconType.cutlery; color: Theme.tintColor
             anchors.verticalCenter: parent.verticalCenter
           }
           AppTextField {
             id: foodInput
             text: !!userCustomData.food ? userCustomData.food : ""
             width: userDetailCol.width - parent.spacing - inputIcon.width
             placeholderText: "Enter your favorite food."
             borderWidth: px(1)
           }
         }
       }

       // save button
       SocialViewButton {
         text: "Save"
         onClicked: {
           var customData = JSON.stringify({ "song": songInput.text, "food": foodInput.text })
           gameNetworkItem.updateUserCustomData(customData) // store stringified JSON data
         }
       }
     }

     // column visible when viewing other users, show data of custom fields
     Column {
       id: otherUserCol
       x: dp(Theme.navigationBar.defaultBarItemPadding) // add indent
       width: parent.width - 2 * x
       spacing: x
       // only show if profile of other user
       visible: gameNetworkUser.userId !== gameNetworkItem.user.userId 

       // show custom data
       Grid {
         spacing: parent.spacing
         columns: 2
         Icon { icon: IconType.music; color: Theme.tintColor }
         AppText { text: !!userCustomData.song ? userCustomData.song : "" }
         Icon { icon: IconType.cutlery; color: Theme.tintColor }
         AppText { text: !!userCustomData.food ? userCustomData.food : "" }
       }
     }
   }
 }

With a single API call, you can load or store JSON objects to the cloud storage. The above example consists of two bigger Column elements. The first one holds all input fields for entering and storing the data. It is only shown when viewing your own profile. Other users see the second item, which only displays the data.

SocialView Profile User Details

With this, your app offers a customized profile view and stores personal user details. But there are much more customization options to extend the SocialView. You can provide a unique user experience for your app with little effort. To learn more about the SocialView and available customization features, see this blog post.

Create Your Own App with Gamification in Minutes!

Gamification features like an in-app chat, a friend system or leaderboards increase app retention. As shown above, V-Play Engine makes it easy to add such features with the SocialView.

SocialView Inbox Chat and UserSearch

SocialView Leaderboard Profile and User Search

You can copy the full example code with all features mentioned above from below. It also shows how to add custom pages to the view and only requires ~230 lines of code!

import VPlay 2.0
import VPlayApps 1.0
import QtQuick 2.9

App {

 // app navigation
 Navigation {
   NavigationItem {
     title: "User Profile"
     icon: IconType.user
     NavigationStack {
       initialPage: socialView.profilePage
     }
   }

   NavigationItem {
     title: "Leaderboard"
     icon: IconType.flagcheckered
     NavigationStack {
       initialPage: socialView.leaderboardPage
     }
   }

   NavigationItem {
     title: "Chat"
     icon: IconType.comment
     NavigationStack {
       initialPage: socialView.inboxPage
     }
   }

   NavigationItem {
     title: "Search"
     icon: IconType.search
     NavigationStack {
       initialPage: mySearchPage
     }
   }
 }

 // service configuration
 VPlayGameNetwork {
   id: gameNetwork
   gameId: 285
   secret: "AmazinglySecureGameSecret"
   multiplayerItem: multiplayer

   // increase leaderboard score by 1 for each app start
   Component.onCompleted: gameNetwork.reportScore(1)
 }

 VPlayMultiplayer {
   id: multiplayer
   appKey: "dd7f1761-038c-4722-9f94-812d798cecfb"
   pushKey: "a4780578-5aad-4590-acbe-057c232913b5"
   gameNetworkItem: gameNetwork
 }

 // social view setup
 SocialView {
   id: socialView
   gameNetworkItem: gameNetwork
   multiplayerItem: multiplayer
   visible: false // we show the view pages on our custom app navigation

   // add button to user profile, which opens a custom user detail page
   profileUserDelegate: SocialUserDelegate {
     id: userDelegate
     SocialViewButton {
       anchors.horizontalCenter: !!parent ? parent.horizontalCenter : null
       text: "Show User Details"
       onClicked: {
         // show custom social page and pass gameNetworkUser
         parentPage.navigationStack.push(userDetailPage, { gameNetworkUser: userDelegate.gameNetworkUser  })
       }
     }
   }
 }

 // page for entering and showing user details
 Component {
   id: userDetailPage
   SocialPage {
     title: "User Details"
     // add a property for the user we want to show on the page
     property GameNetworkUser gameNetworkUser: null

     // parse the JSON data stored in customData property, if it is set
     property var userCustomData: !!gameNetworkUser && !!gameNetworkUser.customData ? JSON.parse(gameNetworkUser.customData) : {}

     // for logged-in user, allow editing the custom fields
     Column {
       id: userDetailCol
       x: dp(Theme.navigationBar.defaultBarItemPadding) // add indent
       y: x // padding top
       width: parent.width - 2 * x
       spacing: x
       visible: gameNetworkUser.userId === gameNetworkItem.user.userId // only show if profile of logged-in user

       AppText {
         text: "Edit the fields below to set your details."
       }

       // custom data fields
       Column {
         spacing: parent.spacing

         Row {
           spacing: parent.spacing
           Icon {
             id: inputIcon
             icon: IconType.music; color: Theme.tintColor
             anchors.verticalCenter: parent.verticalCenter
           }
           AppTextField {
             id: songInput
             text: !!userCustomData.song ? userCustomData.song : ""
             width: userDetailCol.width - parent.spacing - inputIcon.width
             placeholderText: "Enter your favorite music genre."
             borderWidth: px(1)
           }
         }

         Row {
           spacing: parent.spacing
           Icon {
             icon: IconType.cutlery; color: Theme.tintColor
             anchors.verticalCenter: parent.verticalCenter
           }
           AppTextField {
             id: foodInput
             text: !!userCustomData.food ? userCustomData.food : ""
             width: userDetailCol.width - parent.spacing - inputIcon.width
             placeholderText: "Enter your favorite food."
             borderWidth: px(1)
           }
         }
       }

       // save button
       SocialViewButton {
         text: "Save"
         onClicked: {
           var customData = JSON.stringify({ "song": songInput.text, "food": foodInput.text })
           gameNetworkItem.updateUserCustomData(customData)
         }
       }
     }

     // for other users, show data of custom fields
     Column {
       x: dp(Theme.navigationBar.defaultBarItemPadding) // add indent
       y: x // padding top
       width: parent.width - 2 * x
       spacing: x
       visible: gameNetworkUser.userId !== gameNetworkItem.user.userId // only show if profile of other user

       // show custom data
       Grid {
         spacing: parent.spacing
         columns: 2
         Icon { icon: IconType.music; color: Theme.tintColor }
         AppText { text: !!userCustomData.song ? userCustomData.song : "" }
         Icon { icon: IconType.cutlery; color: Theme.tintColor }
         AppText { text: !!userCustomData.food ? userCustomData.food : "" }
       }
     }
   }
 }

 // page for searching users based on entered details
 Component {
   id: mySearchPage
   SocialUserSearchPage {
     filterToUsersWithCustomData: true // only show results for users with custom data
     userSearchUserDelegate: SocialUserDelegate {
       height: userSearchCol.height + 2 * userSearchCol.spacing

       // parse the JSON data stored in customData property, if it is set
       property var userCustomData: !!gameNetworkUser && !!gameNetworkUser.customData ? JSON.parse(gameNetworkUser.customData) : {}

       // background of user item
       Rectangle {
         anchors.fill: parent
         color: "white"
         border.width: px(1)
         border.color: socialViewItem.separatorColor
       }

       // show user details
       Column {
         id: userSearchCol
         x: dp(Theme.navigationBar.defaultBarItemPadding) // add indent
         y: x // padding top
         width: parent.width - 2 * x
         spacing: x
         anchors.verticalCenter: parent.verticalCenter

         // profile image + user name
         Row {
           spacing: parent.spacing
           SocialUserImage {
             height: dp(26)
             width: height
             source: gameNetworkUser.profileImageUrl
           }
           AppText {
             text: gameNetworkUser.name;
             anchors.verticalCenter: parent.verticalCenter
           }
         }

         // show custom data
         Grid {
           spacing: parent.spacing
           columns: 2
           Icon { icon: IconType.music; color: Theme.tintColor }
           AppText { text: !!userCustomData.song ? userCustomData.song : "" }
           Icon { icon: IconType.cutlery; color: Theme.tintColor }
           AppText { text: !!userCustomData.food ? userCustomData.food : "" }
         }
       }

       // open profile when selected
       MouseArea {
         anchors.fill: parent
         onClicked: socialViewItem.pushProfilePage(gameNetworkUser, parentPage.navigationStack)
       }
     }
   }
 }
}

You can also find the full source code of this example on GitHub:

 

To try out the SocialView and test all features, have a look at this year’s Qt World Summit Conference App:

Qt World Summit 2017 Business Meet and Chat

You can download the app for iOS and Android here:
App Store Google_Play_Badge-1

The full source code of the Qt World Summit demo is also available on GitHub:

 

 

 

More Posts Like This

 

Release 2.14.1: Update to Qt 5.9.3 | Use Live Code Reloading on macOS and Linux
V-Play Update 2.12.1: Qt Quick Designer Improvements

How to Make Cross-Platform Mobile Apps with Qt – V-Play Apps

How to Make a Qt app

The post How to Add Chat Service and Cross-Platform Leaderboard with User Profiles to Your iOS or Android App appeared first on V-Play Engine.

Writing a Custom Qt 3D Aspect – part 2

Introduction

In the previous article we gave an overview of the process for creating a custom aspect and showed how to create (most of) the front end functionality. In this article we shall continue building our custom aspect by implementing the corresponding backend types, registering the types and setting up communication from the frontend to the backend objects. This will get us most of the way there. The next article will wrap up by showing how to implement jobs to process our aspect's components.

As a reminder of what we are dealing with, here's the architecture diagram from part 1:

Creating the Backend

One of the nice things about Qt 3D is that it is capable of very high throughput. This is achieved by way of using jobs executed on a threadpool in the backend. To be able to do this without introducing a tangled web of synchronisation points (which would limit the parallelism), we make a classic computer science trade-off and sacrifice memory for the benefit of speed. By having each aspect work on its own copy of the data, it can schedule jobs safe in the knowledge that nothing else will be trampling all over its data.

This is not as costly as it sounds. The backend nodes are not derived from QObject. The base class for backend nodes is Qt3DCore::QBackendNode, which is a pretty lightweight class. Also, note that aspects only store the data that they specifically care about in the backend. For example, the animation aspect does not care about which Material component an Entity has, so no need to store any data from it. Conversely, the render aspect doesn't care about Animation clips or Animator components.

In our little custom aspect, we only have one type of frontend component, FpsMonitor. Logically, we will only have a single corresponding backend type, which we will imaginatively call FpsMonitorBackend:

[sourcecode lang="cpp" title="fpsmonitorbackend.h"]
class FpsMonitorBackend : public Qt3DCore::QBackendNode
{
public:
FpsMonitorBackend()
: Qt3DCore::QBackendNode(Qt3DCore::QBackendNode::ReadWrite)
, m_rollingMeanFrameCount(5)
{}

private:
void initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change) override
{
// TODO: Implement me!
}

int m_rollingMeanFrameCount;
};
[/sourcecode]

The class declaration is very simple. We subclass Qt3DCore::QBackendNode as you would expect; add a data member to mirror the information from the frontend FpsMonitor component; and override the initializeFromPeer() virtual function. This function will be called just after Qt 3D creates an instance of our backend type. The argument allows us to get at the data sent from the corresponding frontend object as we will see shortly.

Registering the Types

We now have simple implementations of the frontend and backend components. The next step is to register these with the aspect so that it knows to instantiate the backend node whenever a frontend node is created. Similarly for destruction. We do this by way of an intermediary helper known as a node mapper.

To create a node mapper, just subclass Qt3DCore::QNodeMapper and override the virtuals to create, lookup and destroy the backend objects on demand. The manner in which you create, store, lookup and destroy the objects is entirely up to you as a developer. Qt 3D does not impose any particular management scheme upon you. The render aspect does some fairly fancy things with bucketed memory managers and aligning memory for SIMD types, but here we can do something much simpler.

We will store pointers to the backend nodes in a QHash within the CustomAspect and index them by the node's Qt3DCore::QNodeId. The node id is used to uniquely identify a given node, even between the frontend and all available aspect backends. On Qt3DCore::QNode the id is available via the id() function, whereas for QBackendNode you access it via the peerId() function. For the two corresponding objects representing the component, the id() and peerId() functions return the same QNodeId value.

Let's get to it and add some storage for the backend nodes to the CustomAspect along with some helper functions:

[sourcecode lang="cpp" title="customaspect.h"]
class CustomAspect : public Qt3DCore::QAbstractAspect
{
Q_OBJECT
public:
...
void addFpsMonitor(Qt3DCore::QNodeId id, FpsMonitorBackend *fpsMonitor)
{
m_fpsMonitors.insert(id, fpsMonitor);
}

FpsMonitorBackend *fpsMonitor(Qt3DCore::QNodeId id)
{
return m_fpsMonitors.value(id, nullptr);
}

FpsMonitorBackend *takeFpsMonitor(Qt3DCore::QNodeId id)
{
return m_fpsMonitors.take(id);
}
...

private:
QHash<Qt3DCore::QNodeId, FpsMonitorBackend *> m_fpsMonitors;
};
[/sourcecode]

Now we can implement a simple node mapper as:

[sourcecode lang="cpp" title="fpsmonitorbackend.h"]
class FpsMonitorMapper : public Qt3DCore::QBackendNodeMapper
{
public:
explicit FpsMonitorMapper(CustomAspect *aspect);

Qt3DCore::QBackendNode *create(const Qt3DCore::QNodeCreatedChangeBasePtr &change) const override
{
auto fpsMonitor = new FpsMonitorBackend;
m_aspect->addFpsMonitor(change->subjectId(), fpsMonitor);
return fpsMonitor;
}

Qt3DCore::QBackendNode *get(Qt3DCore::QNodeId id) const override
{
return m_aspect->fpsMonitor(id);
}

void destroy(Qt3DCore::QNodeId id) const override
{
auto fpsMonitor = m_aspect->takeFpsMonitor(id);
delete fpsMonitor;
}

private:
CustomAspect *m_aspect;
};
[/sourcecode]

To finish this piece of the puzzle, we now need to tell the aspect about how these types and the mapper relate to each other. We do this by calling QAbstractAspect::registerBackendType() template function, passing in a shared pointer to the mapper that will create, find, and destroy the corresponding backend nodes. The template argument is the type of the frontend node for which this mapper should be called. A convenient place to do this is in the constructor of the CustomAspect. In our case it looks like this:

[sourcecode lang="cpp" title="customaspect.cpp"]
CustomAspect::CustomAspect(QObject *parent)
: Qt3DCore::QAbstractAspect(parent)
{
// Register the mapper to handle creation, lookup, and destruction of backend nodes
auto mapper = QSharedPointer<FpsMonitorMapper>::create(this);
registerBackendType<FpsMonitor>(mapper);
}
[/sourcecode]

And that's it! With that registration in place, any time an FpsMonitor component is added to the frontend object tree (the scene), the aspect will lookup the node mapper for that type of object. Here, it will find our registered FpsMonitorMapper object and it will call its create() function to create the backend node and manage its storage. A similar story holds for the destruction (technically, it's the removal from the scene) of the frontend node. The mapper's get() function is used internally to be able to call virtuals on the backend node at appropriate points in time (e.g. when properties notify that they have been changed).

The Frontend-Backend Communications

Now that we are able to create, access and destroy the backend node for any frontend node, let's see how we can let them talk to each other. There are 3 main times the frontend and backend nodes communicate with each other:

  1. Initialisation — When our backend node is first created we get an opportunity to initialise it with data sent from the frontend node.
  2. Frontend to Backend — Typically when properties on the frontend node get changed we want to send the new property value to the backend node so that it is operating on up to date information.
  3. Backend to Frontend — When our jobs process the data stored in the backend nodes, sometimes this will result in updated values that should be sent to the frontend node.

Here we will cover the first two cases. The third case will be deferred until the next article when we introduce jobs.

Backend Node Initialisation

All communication between frontend and backend objects operates by sending sub-classed Qt3DCore::QSceneChanges. These are similar in nature and concept to QEvent but the change arbiter that processes the changes has the opportunity to manipulate them in the case of conflicts from multiple aspects, re-order them into priority, or any other manipulations that may be needed in the future.

For the purpose of initialising the backend node upon creation, we use a Qt3DCore::QNodeCreatedChange which is a templated type that we can use to wrap up our type-specific data. When Qt 3D wants to notify the backend about your frontend node's initial state, it calls the private virtual function QNode::createNodeCreationChange(). This function returns a node created change containing any information that we wish to access in the backend node. We have to do it by copying the data rather than just dereferencing a pointer to the frontend object because by the time the backend processes the request, the frontend object may have been deleted - i.e. a classic data race. For our simple component our implementation looks like this:

[sourcecode lang="cpp" title="fpsmonitor.h"]
struct FpsMonitorData
{
int rollingMeanFrameCount;
};
[/sourcecode]

[sourcecode lang="cpp" title="fpsmonitor.cpp"]
Qt3DCore::QNodeCreatedChangeBasePtr FpsMonitor::createNodeCreationChange() const
{
auto creationChange = Qt3DCore::QNodeCreatedChangePtr<FpsMonitorData>::create(this);
auto &data = creationChange->data;
data.rollingMeanFrameCount = m_rollingMeanFrameCount;
return creationChange;
}
[/sourcecode]

The change created by our frontend node is passed to the backend node (via the change arbiter) and gets processed by the initializeFromPeer()
virtual function
:

[sourcecode lang="cpp" title="fpsmonitorbackend.cpp"]
void FpsMonitorBackend::initializeFromPeer(const Qt3DCore::QNodeCreatedChangeBasePtr &change)
{
const auto typedChange = qSharedPointerCast<Qt3DCore::QNodeCreatedChange<FpsMonitorData>>(change);
const auto &data = typedChange->data;
m_rollingMeanFrameCount = data.rollingMeanFrameCount;
}
[/sourcecode]

Frontend to Backend Communication

At this point, the backend node mirrors the initial state of the frontend node. But what if the user changes a property on the frontend node? When that happens, our backend node will hold stale data.

The good news is that this is easy to handle. The implementation of Qt3DCore::QNode takes care of the first half of the problem for us. Internally it listens to the Q_PROPERTY notification signals and when it sees that a property has changed, it creates a QPropertyUpdatedChange for us and dispatches it to the change arbiter which in turn delivers it to the backend node's sceneChangeEvent() function.

So all we need to do as authors of the backend node is to override this function, extract the data from the change object and update our internal state. Often you will then want to mark the backend node as dirty in some way so that the aspect knows it needs to be processed next frame. Here though,
we will just update the state to reflect the latest value from the frontend:

[sourcecode lang="cpp" title="fpsmonitorbackend.cpp"]
void FpsMonitorBackend::sceneChangeEvent(const Qt3DCore::QSceneChangePtr &e)
{
if (e->type() == Qt3DCore::PropertyUpdated) {
const auto change = qSharedPointerCast<Qt3DCore::QPropertyUpdatedChange>(e);
if (change->propertyName() == QByteArrayLiteral("rollingMeanFrameCount")) {
const auto newValue = change->value().toInt();
if (newValue != m_rollingMeanFrameCount) {
m_rollingMeanFrameCount = newValue;
// TODO: Update fps calculations
}
return;
}
}
QBackendNode::sceneChangeEvent(e);
}
[/sourcecode]

If you don't want to use the built in automatic property change dispatch of Qt3DCore::QNode then you can disable it by wrapping the property notification signal emission in a call to QNode::blockNotifications(). This works in exactly the same manner as QObject::blockSignals() except that it only blocks sending the notifications to the backend node, not the signal itself. This means that other connections or property bindings that rely upon your signals will still work.

If you block the default notifications in this way, then you need to send your own to ensure that the backend node has up to date information. Feel free to subclass any class in the Qt3DCore::QSceneChange hierarchy and bend it to your needs. A common approach is to subclass Qt3DCore::QStaticPropertyUpdatedChangeBase,
which handles the property name and in the subclass add a strongly typed member for the property value payload. The advantage of this over the built-in mechanism is that it avoids using QVariant which does suffer a little in highly threaded contexts in terms of performance. Usually though, the frontend properties don't change too frequently and the default is fine.

Summary

In this article we have shown how to implement most of the backend node; how to register the node mapper with the aspect to create, lookup and destroy backend nodes; how to initialise the backend node from the frontend node in a safe way and also how to keep its data in sync with the frontend.

In the next article we will finally make our custom aspect actually do some real (if simple) work, and learn how to get the backend node to send updates to the frontend node (the mean fps value). We will ensure that the heavy lifting parts get executed in the context of the Qt 3D threadpool so that you get an idea of how it can scale. Until next time. continue reading

The post Writing a Custom Qt 3D Aspect – part 2 appeared first on KDAB.

Looking for Qt Champions

The end of the year is closing in fast, and it is time to look back at 2017 and think who helped you out most during the past year.

In case you forgot, Qt Champions are the people in the community who go above and beyond the normal activity in the community, and help people out every time. They are the stars that make you feel at home.

It’s time to nominate the Qt Champions for 2017. Please take a minute to think fill in the name and reasons you think this person is a Qt Champion.

Qt Champions can be found in all walks of the Qt Community. The categories we have are:

  • Community Builder
  • Content Creator
  • Quality Assurer
  • Developer
  • Ambassador
  • Rookie of the year
  • Maverick

However if you think someone should be raised in several categories, or has done something that does not fit in these categories, please mention that!

We’ll keep the nominations open until the end of 2017 and then ask the current Qt Champions to evaluate the nominees.

Qt Champions receive hand picked Qt Champion items and a full Qt license for a year. So nominating someone is a nice way of saying Thank you!

Qt Champions

 

The post Looking for Qt Champions appeared first on Qt Blog.

Towards an Improved Qt 3D Studio Runtime

Now that Qt 3D Studio 1.0 has been released, it is time to have a sneak peek at some of the upcoming developments. As outlined in the release announcement, there is work on-going to move the runtime, meaning the engine that renders and runs the scenes, on top of Qt 3D. Let’s take a closer look at this.

Overview

Scenes and Layers

Qt 3D Studio is a design tool that allows rapid development of 3D scenes, focused on, but not limited to, 3D user interfaces. Once assets like 3D models and texture maps are imported, designers create scenes by placing and transforming 3D models, applying materials, and setting up keyframe-based animations that target properties of the models, materials and layers. The concept of layers map naturally to what one may be familiar with from tools like Photoshop: each layer contains a 3D scene with its own camera. These are then composited together based on their position, size and blending settings, thus forming the final output of the rendering.

On the layer level there are multiple antialiasing techniques available, like multisampling, supersampling, progressive and temporal antialiasing. See the documentation for an overview of these.

Slides

All this is complemented by a slide system, not unlike presentation tools like Powerpoint. A slide could be thought of as a state: it defines the set of active (visible) objects, the property changes that get applied to the various scene objects, and the set of animations that start when entering the slide in question. This is complemented by the concept of the master slide, which allows defining a set of objects and animations that are present on all slides.

Materials

When the default material, that provides pixel-based lighting, directional, point and area lights, shadow mapping, screen space ambient occlusion, image-based lighting and a number of other features, is not sufficient, custom materials can be applied. These provide custom (fragment) shader code together with a set of properties that form the input to the shaders. Such properties are editable and animatable in the editor just like the built-in ones. While many custom materials will contain a single shader, they can also contain multiple ones, thus defining multiple passes that run in order, each of them further processing the results of the previous passes.

Effects

To apply effects on the content of a given layer, post-processing effects can be used. These are similar to custom materials, but take the output of the 3D rendering from a given layer as their input. Conceptually they map to the ShaderEffect items of Qt Quick but are somewhat more powerful.

Sub-presentations

While one presentation (a single .uip file) describes a single scene (albeit with multiple layers, hence it is more like a 2D composition of multiple 3D scenes), it is possible to have multiple presentations loaded and run in parallel. Here one presentation serves as the “main” one, which is the presentation that gets rendered to the screen. The others serve as sub-presentations that are first rendered offscreen, and then used as texture maps in the materials of the main scene. They can also be used as the source for one or more of the layers of the main presentation.

Building on this, Qt 3D Studio also offers interoperation with Qt Quick. This is achieved by the familiar QQuickRenderControl. This means that interactive Qt Quick scenes can be displayed inside the Qt 3D Studio scene.

This list, while already long enough, does not cover everything. See the documentation for more details.

Qt 3D Studio in action

Qt 3D Studio in action

The screenshot shows many of the facilities mentioned above:

  • The slide management pane on the left,
  • the pane on the right that displays either basic objects (that can be dragged into the scene) or the presentation and asset browser (where 3D models and texture maps are dragged and dropped in order to import assets, and then dragged into the scene or the scene browser below),
  • the bottom pane contains the scene browser (note how the example has two layers, each with its own camera, lights and models) and the timeline that is used to define and edit keyframes,
  • the bottom-right pane, showing the properties for the currently selected model, material or other object.

What’s in a Runtime?

The main editor application is complemented by the so-called runtime component, which consists of the C++ and OpenGL-based engine that renders and runs the presentations created with the editor both in the viewer application shipped with Qt 3D Studio and in any other Qt applications. The APIs provided allow integrating Qt 3D Studio scenes into Qt Quick, QWidget and QWindow-based applications, and also provide facilities for rendering completely offscreen in order to generate videos for example. The rendering APIs are complemented by a set of QML and C++ APIs that allow changing properties of the scene objects at runtime and controlling the slide and animation system. See the links to the documentation for more details.

Qt 3D Studio Viewer example

The same scene in the standalone Qt 3D Studio Viewer application

For an impression of what the APIs Qt application developers would use look like, let’s look at the source of two of the included examples. First, a straightforward pure QWindow-based application (can be found under example/studio3d/surfaceviewer):

#include <QtStudio3D/Q3DSSurfaceViewer>
#include <QtStudio3D/Q3DSViewerSettings>
#include <QtStudio3D/Q3DSPresentation>
#include <QtStudio3D/Q3DSSceneElement>
#include <QtStudio3D/Q3DSElement>
...
int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QWindow window;

    QSize size(1200, 800);
    window.resize(size);
    window.setSurfaceType(QSurface::OpenGLSurface);
    window.setTitle(QStringLiteral("Qt 3D Studio surface viewer example"));
    window.create();

    QOpenGLContext context;
    context.setFormat(window.format());
    context.create();

    Q3DSSurfaceViewer viewer;
    viewer.presentation()->setSource(QUrl(QStringLiteral("qrc:/presentation/circling_cube.uip")));
    viewer.setUpdateInterval(0);
    viewer.settings()->setScaleMode(Q3DSViewerSettings::ScaleModeFill);
    viewer.settings()->setShowRenderStats(true);

    Q3DSSceneElement sceneElement(viewer.presentation(), QStringLiteral("Scene"));
    Q3DSElement counterElement(viewer.presentation(), QStringLiteral("Scene.Layer.Loopcounter"));

    viewer.initialize(&window, &context);

    window.show();

    int n = 0;
    QString loopCounter = QStringLiteral("Loop %1");
    QObject::connect(&sceneElement, &Q3DSSceneElement::currentSlideIndexChanged, [&]() {
        if (sceneElement.currentSlideIndex() == 1)
            n++;
        counterElement.setAttribute(QStringLiteral("textstring"), loopCounter.arg(n));
    });

    return app.exec();
}

In practice one will likely rather use the Qt Quick integration, at the core of which stands the Studio3D element. Under the hood this is built on QQuickFramebufferObject. (the snippet here is from examples/studio3d/qmldynamickeyframes).

import QtQuick 2.7
import QtStudio3D 1.0

Item {
    ...
    
    Studio3D {
        id: studio3D
        anchors.fill: parent

        // ViewerSettings item is used to specify presentation independent viewer settings.
        ViewerSettings {
            scaleMode: ViewerSettings.ScaleModeFill
            showRenderStats: false
        }

        // Presentation item is used to control the presentation.
        Presentation {
            source: "qrc:/presentation/dyn_key.uip"

            // SceneElement item is used to listen for slide changes of a scene in the presentation.
            // You can also change the slides via its properties.
            SceneElement {
                id: scene
                elementPath: "Scene"
                onCurrentSlideIndexChanged: {
                    console.log("Current slide : " + currentSlideIndex + " ("
                                + currentSlideName + ")");
                }
                onPreviousSlideIndexChanged: {
                    console.log("Previous slide: " + previousSlideIndex + " ("
                                + previousSlideName + ")");
                }
            }

            // Element item is used to change element attributes
            Element {
                id: materialElement
                elementPath: "Scene.Layer.Sphere.Material"
            }

            property int desiredSlideIndex: 1
            property int colorIndex: 0
            property var colorArray: [
                [1.0, 1.0, 1.0],
                [1.0, 0.0, 0.0],
                [0.0, 1.0, 0.0],
                [0.0, 0.0, 1.0],
                [0.0, 1.0, 1.0],
                [1.0, 0.0, 1.0],
                [1.0, 1.0, 0.0]
            ]

            function nextSlide() {
                // Separate desiredSlideIndex variable is used to keep track of the desired slide,
                // because SceneElement's currentSlideIndex property works asynchronously.
                // This way the button click always changes the slide even if you click
                // it twice during the same frame.
                desiredSlideIndex = desiredSlideIndex != 3 ? desiredSlideIndex + 1 : 1;
                scene.currentSlideIndex = desiredSlideIndex
                slideButtonText.text = "Change Slide (" + desiredSlideIndex + ")"
            }

            function resetTime() {
                scene.goToTime(0);
            }

            function changeColor() {
                colorIndex = colorIndex >= colorArray.length - 1 ? colorIndex = 0 : colorIndex + 1;
                materialElement.setAttribute("diffuse.r", colorArray[colorIndex][0]);
                materialElement.setAttribute("diffuse.g", colorArray[colorIndex][1]);
                materialElement.setAttribute("diffuse.b", colorArray[colorIndex][2]);
                changeColorButton.color = Qt.rgba(colorArray[colorIndex][0],
                                                  colorArray[colorIndex][1],
                                                  colorArray[colorIndex][2], 1.0);
            }
        }
        onRunningChanged: console.log("Presentation running")
    }
    ...

(Before anyone asks: The ability to embed the output of the various Qt UI frameworks into each other opens up the possibility for creative recursive solutions indeed. However, while the idea of embedding a Qt Quick scene into a Qt 3D Studio scene embedded again in Qt Quick which then perhaps gets embedded into a plain Qt 3D scene which in turn can be part of another Qt Quick scene, … may sound exciting at first, it is best not to overdo it.)

Of course, the editor application itself also needs to display, edit and have fine-grained control over the scene. In the current iteration (1.0) not everything is unified in this respect, meaning the way the editor renders under the hood is not necessarily based on the same code as in the viewer or in the external applications. Longer term we expect to have a more unified approach in place. For now, let’s focus on the runtime from the perspective of a typical Qt application.

Towards Qt 3D Studio 2.0

You said C++ and OpenGL. Good enough, no?

The 1.0 runtime is pretty portable already and it comes with the necessary Qt integration bits as shown above. However, as it is based on the code inherited from the original NVIDIA contribution with only certain parts Qt-ified, and with a strong dependency to OpenGL, there is room for improvement. Fortunately, it turns out we have a good way forward – while staying fully compatible with presentations created with the already released version 1.0.

Qt 3D 2.0 (not to be confused with the Qt 3D 1.0 project from Qt 4 times) has been introduced to Qt 5 by developers from KDAB. See this post for an introduction. Besides providing the necessary building blocks, like an entity-component system and the concept of framegraphs that turn out to be an excellent tool for describing how to render multi-pass 3D scenes in a data-driver manner, its abstractions become handy also when looking towards the future where graphics APIs other than OpenGL will play an increasingly important role.

Therefore, back in April we started a little experiment under the Dragon3 internal codename to see what it would take to load and run those .uip presentations on top of Qt 3D. The new code base is developed with the familiar Qt style and conventions, thus providing a better, more maintainable, and more future-proof component in the larger Qt ecosystem. This is the project now often referred to as the Qt 3D Studio Runtime 2.0, with a first release due around May 2018 together with Qt 3D Studio 2.0. Like Qt 3D Studio itself, the code is available in public already today, although it does come with a huge disclaimer of being heavily work in progress.

Should I wait then?

Not at all. As mentioned before, compatibility is a priority, so for all practical purposes the 1.0 release is the thing to use. Naturally those who feel like experimenting are welcome to check out the code from the qt3d-runtime repository as well (which needs the dev (eventually 5.11) branch of qtbase and qt3d to build).

Let’s continue in the next post. In the meantime those who cannot wait and are keen on diving into the juicy details can check out my presentation at the Qt World Summit earlier this year.

That’s all for now, happy 3D hacking!

The post Towards an Improved Qt 3D Studio Runtime appeared first on Qt Blog.

Qbs 1.10 released

Qbs (pronounced “Cubes”) is The Qt Company’s latest build tool, which aims to be the replacement for qmake in the Qt build system. In addition to its use within Qt, for our customers Qbs will also provide faster build times coupled with a rich feature set in order to reduce build engineering complexity and decrease time to market.

Today I’m excited to introduce you to the latest release of Qbs – 1.10.

What’s New?

This release focuses on bug fixing and general enhancements, specifically to the Qbs language.

Language Improvements

Profiles can now be defined dynamically in a project using the new Profile language item. This is useful for when a project has specific, well-known requirements regarding the build environment and/or target platform.

Group nesting now works more naturally with respect to prefixes. If a Group does not define the prefix property, its value will be taken from the parent group (and so on until a prefix is found or the product level is reached).

Modules and file taggers may now set priority levels. This allows Qbs to disambiguate between multiple implementations of the same module whose conditions evaluate to true, or multiple file taggers that would tag the same files – essentially, a conflict resolution mechanism. Simply set the priority property of a Module or FileTagger to an integer value greater than zero. The instance with the highest value wins. For example, Qbs uses this to choose the cpp module implementation on different platforms. Because the Android implementation is a superset of the Linux implementation, both the Linux and Android implementations are available on Android, but because the latter has a higher priority level, that implementation gets chosen.

It is now possible to add file tags to generated artifacts by setting the new fileTags property in a group that has a fileTagsFilter. This allows you to “append” to the list of file tags assigned to artifacts created by a rule that is defined in a module which is outside your control.

Documentation

Another big focus of this release was enhancing the documentation (and even more significant changes in this area will come with Qbs 1.11).

To start with, we’ve added two new major pages. The first is a qmake porting guide, which contains an overview of various qmake variables and how to achieve the same functionality in Qbs. The second is a “how-to” page on how to create modules for third party libraries. We plan to have many more of these “how-to” style guides showing how to solve common build system problems with Qbs.

We also added in-depth descriptions of all command line tools and their respective options, and a man page for the qbs command line tool.

Platform Support

This release adds a few notable platform-specific features: initial support for the Universal Windows Platform, and the run command can now deploy and run Android apps on devices, and deploy and run iOS and tvOS apps on the simulator.

Qt Support

This release adds support for the Qt Quick compiler and for qmlcachegen.

Other New Features

  • The TextFile service can now append to existing files using the new TextFile.Append open mode.
  • Process and TextFile objects in rules, commands and configure scripts are now closed automatically after script execution.
  • Added the vcs module to provide VCS repository information. Git and Subversion are supported initially.
  • Added the cpufeatures module for abstracting compiler flags related to CPU features such as SSE.
  • Added property cpp.discardUnusedData abstracting linker options that strip unneeded symbols or sections.
  • Added property cpp.variantSuffix for making binary names unique when multiplexing products.
  • Added property cpp.compilerDefinesByLanguage providing the compiler’s pre-defined macros.
  • Added new command line sub-command list-products which lists the names of the products available in the project.

Try It!

The Open Source version is available on the download page, and you can find commercially licensed packages on the Qt Account Portal. Please post issues in our bug tracker. You can also find us on IRC in #qbs on chat.freenode.net, and on the mailing list. The documentation and wiki are also good places to get started.

Qbs is also available on a number of packaging systems (Chocolatey, MacPorts, Homebrew) and updated on each release by the Qbs development team. It can also be installed through the native package management system on a number of Linux distributions including but not limited to Debian, Ubuntu, Fedora, and Arch Linux.

Qbs 1.10 is also included in Qt Creator 4.5, which was also released this week.

The post Qbs 1.10 released appeared first on Qt Blog.

Qt 5.10 released

I’m happy to let you all know that Qt 5.10 has just been released. Qt 5.10 comes with a ton of new functionalities that I’m thrilled to talk to you about.

Before we dive into all the great new features of Qt 5.10, I want to talk to you about another exciting new addition to Qt being released today.

Qt 3D Studio

As the name suggests, Qt 3D Studio is a graphical editor used to create of 3D user interfaces and is derived from the NVIDIA Drive Design product that NVIDIA contributed to Qt earlier this year.

Qt 3D Studio consists of both a runtime component that is run in your application and a graphical design tool to design and create the UI. While the tool is a standalone application, the runtime can easily be integrated with the rest of Qt.

Qt 3D Studio will work with both Qt 5.9 and 5.10. The runtime and the 3D Studio application are both available under commercial and GPL licensing. For more details, please have a look at the separate blog post about it.

qt3dstudio_floorplan_preview

Qt Qml, Qt Quick, and Qt Quick Controls

A lot of exciting new things have happened in this area. Qt Quick has now gained a plugin that allows placing arbitrarily shaped items into the scene. Have a look at the blog post and documentation for more details.

shape_tiger

The new pointer handlers are the next big new feature in Qt Quick. This is a large step forward for Qt Quick that significantly improves how to handle many more complex multi-touch use cases. Instead of using Mouse- and TouchArea to handle those input events, you can now attach handlers for different types of pointer events to any item. Support for this is still in technology preview. Please have a look at the blog post and documentation for all the details.

On top of that, Qt Quick gained a larger amount of smaller enhancements. Chief among them the support to directly load ETC1 and ETC2 compressed textures, multisampling support for layers and some properties to tune font handling.

Qt Quick Controls 2 has also received many new functionalities. First of all, there are now two new styles available. One is an image-based style that makes it very easy to use your own artwork to create a custom look and feel. The other one, called Fusion style, gives a platform agnostic, desktop-oriented look and feel. Those styles also support propagating palettes.

Left: Imagine style - Right: Fusion style

Left: Imagine style – Right: Fusion style

We’ve also added Action, ActionGroup and MenuBar items. Outside of MenuBar, Actions are also supported in buttons, delegates, and menus. In addition, we added numerous smaller features to existing controls.

Qt Qml finally supports declaring enums in QML and dynamically retranslates all strings on language changes.

Graphics

A massive new feature in the graphics area is a new QPA plugin that allows streaming a full Qt User Interface to any WebGL enabled browser. You can find details in this blog post.

On the OpenGL side, QSurfaceFormat can now be used to request sRGB-compatible framebuffers, and the full set of the OpenGL ES 3.2 API is now available across platforms through QOpenGLExtraFunctions.

Qt 3D

Thanks to our friends at KDAB, Qt 3D has also gained a couple of new features. There are new node types that can be used in frame graphs, such as a proximity filter and a framebuffer blit. We now have support for Shader graphs that enable the creation of shaders from a graph based description, a technology preview of a skeletal animation system and a Scene2D Qt Quick element, which simplifies embedding Qt 3D content in a Qt Quick Scene.

Apart from that, the focus on Qt 3D development is now slowly shifting from developing all the required features to improving performance and memory consumption. Some of the results of that effort can be seen in the following blog post.

Qt 3D will also become much more important for Qt moving forward. We are currently working on basing the runtime for Qt 3D Studio on top of Qt 3D in order to unify the architecture for all our 3D related APIs and tools.

Qt Core and Network

Qt Core now has a brand new QStringView class, that is conceptually similar to std::string_view. For the moment, it’s still missing some const functions from QString, but that will get finalized for Qt 5.11.

Qt Core also received a new QRandomGenerator class that provides a high-quality random generator and access to seedless random numbers generated with hardware or operating system facilities. There’s now also a set of explicit big/little endian data types (qint16_le, etc), and a KeyValueIterator for our associative containers that provide STL compatible iteration semantics over those containers.

Qt Network now supports OpenSSL 1.1, stores HSTS (HTTP Strict Transport Security) policies in a persistent way in QNetworkAccessManager and has implemented the h2c protocol upgrade used by non-SSL HTTP/2.

Qt Widgets

We have added a couple of smaller features, such as QWidget::createWindowContainer() on Android, enabling QOpenGLWidget for rendering and grabbing offscreen content, adding a tabStopDistance property in QTextOption and QTextEdit and an API for better selection handling to QLineEdit. QDockWidgets can now be dropped side by side.

Additionally, a large set of bugs have been fixed in this module for Qt 5.10.

Embedded functionality

While most of the items above improve our overall offering, both for application development and device creation, we also have some items that focus on the embedded side.

The Qt Virtual Keyboard has gained support for many new languages and handwriting support for Arabic, Farsi, Chinese, Japanese and Korean.

A brand-new version of the device emulator is coming with Qt 5.10. It now supports multiple screens, multitouch and has received support for plugins. Have a look at the separate blog post for details.

On the Boot2Qt side, we updated our device images to Yocto 2.3 (Pyro), giving you a more up-to-date Linux to base your work upon.

Qt for Automation, containing the KNX and MQTT modules, is now also available in Qt 5.10.

Other modules

We updated Qt WebEngine to use Chromium 61. You can now pause and resume downloads and start them programmatically.

On Windows desktop, you can now use the UWP backend for Qt Bluetooth on Windows, and on X11, the Qt X11Extras module now allows peeking into the event queue.

Finally, Qt Network Authorization (providing support for OAuth 1 & 2) and the text-to-speech support in Qt Speech graduated from technology preview to being fully supported in 5.10.

For more details, have a look at the new features page on the wiki, our dedicated Qt 5.10 web page and our change logs for each Qt module.

I hope you will enjoy the release, and I’d like to thank everybody who has contributed to making it happen. As always, you can get this release from your Qt Account page or our download section on the web.

The post Qt 5.10 released appeared first on Qt Blog.

Qt Creator 4.5.0 released

We are happy to announce the release of Qt Creator 4.5.0!

CMake Support

Variables in the configuration UI in the Projects mode are now grouped by prefix, and have type-specific editors for boolean and path values. You can also change the type of variables and unset accidentally added ones. Also, we improved the handling of configuration changes outside of Qt Creator. The change notification now shows detailed information about how the cached values  differ from the values on disk.

Android Support

Integrated Adnroid SDK package manager - Qt Creator 4.4
Since Android SDK tools version 25.3.0, there is no UI tool available to manage the Android SDK packages. Because of that, we have added such UI to Qt Creator. Unfortunately, the command line tool for managing SDKs that is provided with the SDK cannot update packages on Windows, and fails with JDK 9, so this applies to Qt Creator as well. Also, we provide better information about problems with the installed SDK, such as missing components or not met minimum version requirements.

Qt Creator no longer supports local deployment nor Ant, starting with this version. Both features have been deprecated for a long time.

Other Improvements

We started on making the File System navigation pane more useful. It is showing a file system tree now, and you can select the root directory from a list containing the “computer” root, your home directory, your default projects directory, and the base directories of all the projects you have open in Qt Creator. More features are to come in the future.

Locator now does fuzzy camel case matching in the same way as code completion does. For example, you can type “c andesu” in the locator to open the “AndroidDebugSupport” class.

On Windows, we fixed detection of Visual Studio Build Tools 2017.

For iOS, we had to fix our mechanism for switching between simulator device types with Xcode 9, because that changed the behavior of the Simulator application.

If you used Tools > External > Text > Sort Selection before, and wonder if we have removed this useful feature, I can reassure you that we have not. On the contrary, we made it a first-class citizen instead of an external tool, and moved the menu entry to Edit > Advanced > Sort Selected Lines.

This release includes many more improvements and fixes. Please, take a look at our change log for a more detailed overview.

Get Qt Creator 4.5.0

The opensource version is available on the Qt download page, and you find commercially licensed packages on the Qt Account Portal. Qt Creator 4.5.0 is also available through an update in the online installer (as an update to 4.4.1). Please post issues in our bug tracker. You can also find us on IRC on #qt-creator on chat.freenode.net, and on the Qt Creator mailing list.

The post Qt Creator 4.5.0 released appeared first on Qt Blog.

How to Add In-App Chat or Gamification Features to Your Mobile App

Many successful apps allow users to interact, compete or build social relationships. Features like an in-app chat, a friend system or leaderboards help to increase app retention and engagement rates. This applies for games as well as apps. The SocialView makes it easy to add such features. It takes advantage of ready-made services and also offers e.g. user profiles & authentication, cloud storage and much more. If you think this sounds complex, don’t worry: This post shows you how to add such features without a hassle.

Using Social Services is Easy!

… at least with the SocialView, which includes many ready-made social features. This makes it easy for you to create apps with e.g. user profiles, leaderboards or a messaging and friend system. For example, this year’s conference management app for the Qt World Summit 2017 uses these services and allows you to:

  • Search conference attendees to schedule Business Meetings.
  • Store custom user data like the company, job position or main Qt interest.
  • View user details and find conference attendees that match certain criterias.
  • Log in via Facebook to synchronize your data across devices.
  • Become friends and get in touch with the included in-app chat.
  • See a leaderboard with a ranking of users that favor many talks and are active.

business-meet-and-chat

You can have a look and try out the features by downloading the app for iOS or Android:

App Store Google_Play_Badge-1

This is already a rich feature set, but there is much more you can do. The SocialView is extensible and allows to customize all features!

A cloud-based backend and easy-to-use client QML components build the core of the view. The SocialView integrates these cloud services so you can use them in your app. Among many other features, the SocialView services include:

  • User Profiles and Authentication: Each user registers with the social services as soon as he first opens your app. No registration required.
  • Social Leaderboards: You can let players compare against each other in a competitive way, keeping them playing.
  • Fully Customizable UI: With the strength of QML you can customize all views to your app and give an extra-polished look.
  • Integrated Cloud Storage: Store project-specific data for your users in the cloud.
  • Friend System: Add your friends or make new friends with other users you meet in-app.
  • In-App Chat & Push Notifications: You can chat with your friends, even if they’re not online. This makes it easy for your users to get in touch. They also receive Push Notifications for each incoming message.
  • Facebook Login & Account Synchronization: You can allow users to log-in with Facebook. This enables them to synchronize their profile and progress across devices.
  • Native Look and Feel: The view provides a native style and user experience on both iOS and Android.
  • Fully Cross-Platform: All features work cross-platform, so your iOS users can also connect or play with Android users.

mobile-chat

To see how to add such services, have a look at the Qt World Summit 2017 app. It shows how to use the SocialView and enriches it with custom features. The full updated source code is available for you on GitHub.

How to Add User Profiles or an In-App Chat with the SocialView

The SocialView offers a flexible usage for both games and apps. You can add user profiles, leaderboards or the messaging and friend system to any project.

Use the SocialView Standalone

The view contains several pages that each cover a certain use-case:

  • profilePage – shows the user profile a GameNetworkUser
  • leaderboardPage – shows the highscores for a certain leaderboard and time frame
  • inboxPage – shows the active chats of the currently logged-in user
  • chatPage – shows a chat of the logged-in user with another user
  • newMessagePage – shows a user search and allows to start a chat

If not specified otherwise, it shows the user profile of the logged-in user by default. This is how a minimum example setup for a full-featured SocialView integration looks like in QML:

import VPlay 2.0

GameWindow {
 VPlayGameNetwork {
   id: gameNetwork
   gameId: 285
   secret: "AmazinglySecureGameSecret"
   multiplayerItem: multiplayer
 }

 VPlayMultiplayer {
   id: multiplayer
   appKey: "dd7f1761-038c-4722-9f94-812d798cecfb"
   pushKey: "a4780578-5aad-4590-acbe-057c232913b5"
   gameNetworkItem: gameNetwork
 }

 SocialView {
   id: socialView
   gameNetworkItem: gameNetwork
   multiplayerItem: multiplayer
 }
}

No extra code needed. The view also is a full-featured NavigationStack type by itself. So you can configure the initialPage or e.g. push the default leaderboard with simple call to:

socialView.push(socialView.leaderboardPage)

This is the perfect way for integrating the view to V-Play Games.

Add SocialView Pages to your App Navigation

The SocialView contains several individual social pages. They are independent pages, but linked to the SocialView behind the scenes. Each page can work with any NavigationStack layout you might use in your existing V-Play App.

The following code snippet creates an app with two separate navigation tabs. One for the user profile, and one for showing the inbox of the friend and messaging service:

import VPlay 2.0
import VPlayApps 1.0

App {

 VPlayGameNetwork {
   id: gameNetwork
   gameId: 285
   secret: "AmazinglySecureGameSecret"
   multiplayerItem: multiplayer
 }

 VPlayMultiplayer {
   id: multiplayer
   appKey: "dd7f1761-038c-4722-9f94-812d798cecfb"
   pushKey: "a4780578-5aad-4590-acbe-057c232913b5"
   gameNetworkItem: gameNetwork
 }

 SocialView {
   id: socialView
   gameNetworkItem: gameNetwork
   multiplayerItem: multiplayer
   visible: false // hide social view, as we show the pages on custom NavigationStacks instead
 }

 // main navigation
 Navigation {

   // first navigation item: Profile
   NavigationItem {
     title: "Profile"
     icon: IconType.user

     NavigationStack {
       initialPage: socialView.profilePage
     }
   }

   // second navigation item: Chat Inbox
   NavigationItem {
     title: "Chat"
     icon: IconType.comment

     NavigationStack {
       initialPage: socialView.inboxPage
     }
   }
 }
}

This already shows the flexible usage possibilities of the new view. The approach in the above example works with the page properties of the SocialView. This is the fastest way for adding e.g. the own user profile or the default leaderboard to your app navigation.

You can also show a different user profile or leaderboard. The SocialView provides functions that let you pass your desired settings. For example, the following call displays the profile of a certain user:

socialView.pushProfilePage(gameNetworkUser, targetNavigationStack)

The function also allows to specify the target NavigationStack for adding the page. Similar functions exist for all available SocialView pages. So you can integrate the social pages anywhere in your app!

Customize the Features and Look of your Social Services

Each project is different and has its own unique style. To add your own style to the view, there are several customization options you can use. They allow to:

  • Match the view colors and fonts with your project style.
  • Enrich the UI with custom elements and features.
  • Introduce completely new social pages.

The V-Play Apps types build the core of the customizable SocialView UI. This means, it also comes with a native look and feel for Android and iOS and e.g. offers a native back navigation.

Change the View Colors and Fonts

The SocialView offers several properties for the general look of all social pages. It is possible to customize used fonts and colors to match your project style with a few lines of code.

The view tint matches the configured color of your app theme by default. No further adjustments needed if this is the color of your choice for the social pages.

The following example disables the user country feature and sets an orange tint. It also switches to the configured theme font instead of the default social view font:

SocialView {
   gameNetworkItem: gameNetwork
   multiplayerItem: multiplayer

   // custom styling
   tintColor: "orange"
   tintLightColor: Qt.lighter(tintColor, 1.2)
   countryCodeEnabled: false
   bodyFontName: Theme.normalFont.name
   headerFontName: Theme.normalFont.name
 }

Add Custom UI Elements with Delegates

The V-Play Game Network service allows to store project-specific data of your users. In your app, this data is available with the customData property. The Qt World Summit app also sets this field to store personal information, e.g. the Qt interest, on the profile page.

profile-custom-views

But how can you display the UI for editing or showing this information in the user profile?

The SocialUserDelegate type lets you provide the full QML code of such additions. It is set up to represent a single GameNetworkUser, which the view displays in the social pages.

Let’s say you allow users to enter a custom description text somewhere in your app, which you store as custom data. This simple code snippet allows you to display the description in the user profile:

SocialView {
   id: socialView
   gameNetworkItem: gameNetwork
   multiplayerItem: multiplayer

   // show custom user data in profile
   profileUserDelegate: SocialUserDelegate {
     AppText {
       text: gameNetworkUser.customData 

       x: dp(Theme.navigationBar.defaultBarItemPadding) // add indent / padding
       width: parent.width-x
       font.pixelSize: sp(14)
       wrapMode: AppText.WrapAtWordBoundaryOrAnywhere

       // add social view style to text
       font.family: socialViewItem.bodyFontName
       color: socialViewItem.bodyColor
     }
   }
 }

That is all you need! The new custom text now shows up in the user profile page:

Extend the profile page with a custom delegate.

The above example uses the profile user delegate, which is not set to a specific item by default.
Other delegates for e.g. the leaderboard and inbox page are already predefined. Each delegate item represents a single user of the displayed list in this case.

To customize these user lists, you can overwrite the used delegates. This example replaces each score entry of the leaderboard with a custom QML item:

SocialView {
   id: socialView
   gameNetworkItem: gameNetwork
   multiplayerItem: multiplayer

   leaderboardUserDelegate: SocialUserDelegate {
     height: col.height

     // show user name and score below each other
     Column {
       id: col
       anchors.centerIn: parent
       AppText {
         anchors.horizontalCenter: parent.horizontalCenter // center name horizontally
         text: gameNetworkUser.name
       }
       AppText {
         anchors.horizontalCenter: parent.horizontalCenter // center score horizontally

          // it is possible to access each response entry of the related API request using modelData
         text: "Score: " + modelData.value
       }
     }
   }
 }

The new social delegate system is the perfect way to customize the social pages to your liking! But that is not all, you can even throw in completely custom social pages.

How to Create New Social Pages

The ability to add your own delegates also opens another possibility. You can extend the view with custom social pages, which extend the new SocialPage base type. It works together with SocialView to:

  • Offer quick access to the Game Network and Multiplayer features.
  • Show a loading indicator in the navigation bar. Your users will see that there’s a loading process going on whenever there are pending API requests.
  • Apply the configured SocialView styling like the view tint color or body font.

Let’s upgrade the profile page and add a button. It will take us to a new social page, where we show the custom data of the user:

SocialView {
   id: socialView
   gameNetworkItem: gameNetwork
   multiplayerItem: multiplayer

   profileUserDelegate: SocialUserDelegate {
     id: userDelegate
     SocialViewButton {
       anchors.horizontalCenter: parent.horizontalCenter
       text: "Show User Details"
       onClicked: {
         // show custom social page and pass gameNetworkUser
         parentPage.navigationStack.push(userDetailPage, { gameNetworkUser: userDelegate.gameNetworkUser  })
       }
     }
   }
 }

Component {
   id: userDetailPage
   SocialPage {
     // add a property for the user we want to show on the page
     property GameNetworkUser gameNetworkUser: null

     // show custom data
     AppText {
       text: gameNetworkUser.customData

       x: dp(Theme.navigationBar.defaultBarItemPadding) // add indent/padding
       width: parent.width-x
       font.pixelSize: sp(14)
       wrapMode: AppText.WrapAtWordBoundaryOrAnywhere

       // add social view style to text
       font.family: socialViewItem.bodyFontName
       color: socialViewItem.bodyColor
     }
   }
 }

This addition uses the profileUserDelegate to extend the profile page. It adds a button, which takes us to a new custom social page:

Add buttons to integrate custom social pages.

That page only shows the plain string text value of customData. But of course you could do much more. The following example extends the above code to allow saving the favorite song and food for each user. The userDetailPage now shows input fields to edit and store the data for the logged-in user. When viewing the profiles of other users, it just displays the stored values:

import VPlay 2.0
import VPlayApps 1.0
import QtQuick 2.9

App {
 VPlayGameNetwork {
   id: gameNetwork
   gameId: 285
   secret: "AmazinglySecureGameSecret"
   multiplayerItem: multiplayer
 }

 VPlayMultiplayer {
   id: multiplayer
   appKey: "dd7f1761-038c-4722-9f94-812d798cecfb"
   pushKey: "a4780578-5aad-4590-acbe-057c232913b5"
   gameNetworkItem: gameNetwork
 }


 SocialView {
   id: socialView
   gameNetworkItem: gameNetwork
   multiplayerItem: multiplayer

   profileUserDelegate: SocialUserDelegate {
     id: userDelegate
     SocialViewButton {
       anchors.horizontalCenter: parent.horizontalCenter
       text: "Show User Details"
       onClicked: {
         // show custom social page and pass gameNetworkUser
         parentPage.navigationStack.push(userDetailPage, { gameNetworkUser: userDelegate.gameNetworkUser  })
       }
     }
   }
 }

 Component {
   id: userDetailPage
   SocialPage {
     title: "User Details"
     // add a property for the user we want to show on the page
     property GameNetworkUser gameNetworkUser: null

     // parse the JSON data stored in customData property, if it is set
     property var userCustomData: !!gameNetworkUser && !!gameNetworkUser.customData ? JSON.parse(gameNetworkUser.customData) : {}

     // for logged-in user, allow editing the custom fields
     Column {
       x: dp(Theme.navigationBar.defaultBarItemPadding) // add indent
       y: x // padding top
       width: parent.width-x
       spacing: x
       visible: gameNetworkUser.userId === gameNetworkItem.user.userId // only show if profile of logged-in user

       AppText {
         text: "Edit the fields below to set your details."
       }
       AppTextField {
         id: songInput
         text: !!userCustomData.song ? userCustomData.song : ""
         placeholderText: "Enter your favorite song."
       }
       AppTextField {
         id: foodInput
         text: !!userCustomData.food ? userCustomData.food : ""
         placeholderText: "Enter your favorite food."
       }
       SocialViewButton {
         text: "Save"
         onClicked: {
           var customData = JSON.stringify({ "song": songInput.text, "food": foodInput.text })
           gameNetworkItem.updateUserCustomData(customData)
         }
       }
     }

     // for other users, show data of custom fields
     Column {
       x: dp(Theme.navigationBar.defaultBarItemPadding) // add indent
       y: x // padding top
       width: parent.width-x
       spacing: x
       visible: gameNetworkUser.userId !== gameNetworkItem.user.userId // only show if profile of other user

       AppText {
         text: "Favorite Song: "+(!!userCustomData.song ? userCustomData.song : "")
       }
       AppText {
         text: "Favorite Food: "+(!!userCustomData.food ? userCustomData.food : "")
       }
     }
   }
 }
}

 

The detail page now allows to enter and store the custom data:
The final userDetailPage allows to enter your favorite song and food.

You can see all customization features in action with the Qt World Summit 2017 app on GitHub:

GitHub - Qt World Summit 2017 app

Now it is up to you to add customized social features to your apps – the SocialView is definitely the right tool for it!

 

 

More Posts Like This

 

Release 2.14.1: Update to Qt 5.9.3 | Use Live Code Reloading on macOS and Linux
V-Play Update 2.12.1: Qt Quick Designer Improvements

How to Make Cross-Platform Mobile Apps with Qt – V-Play Apps

How to Make a Qt app

The post How to Add In-App Chat or Gamification Features to Your Mobile App appeared first on V-Play Engine.

Release 2.14.2: Live Code Reloading with Native Cross-Platform Plugins

V-Play 2.14.0 introduced V-Play Live Code Reloading, which reduces your deployment time from minutes to seconds. V-Play 2.14.2 now adds support for using native cross-platform plugins with live code reloading.

2.14.2 also adds improvements to the SocialView components and adds improvements for Navigation components.

What is V-Play Live Code Reloading

V-Play Live reloads your project on every connected device, within seconds after code changes. This reduces the long compilation and deployment times, to only a couple of seconds. V-Play Live also allows you to run and test your applications on iOS from a Windows or Linux desktop. You don’t need to install any native SDKs for deployment with V-Play Live.

how-does-v-play-live-work-compared-with-standard-deployment

Watch this video for a quick demo of V-Play Live in action:

 

V-Play Live displays your app or game with the Live Client application. The desktop Live Client is part of your V-Play installation. For Android and iOS, you can download the Live Client apps in the app stores. For most projects those Live Client apps are sufficient. They have several V-Play Plugins and most of the Qt modules configured, so you can use them with V-Play Live.

Soon you will have the option to extend your own project with the live reloading feature, to build custom Live Clients. This allows you to use any custom C++ or native code together with live reloading for QML & JavaScript. This will also make it possible to use even more V-Play Plugins and features with V-Play Live.

How to Use V-Play Live

V-Play Live is super easy to use. We already covered that in our last blog post, you can read it here: How to use V-Play Live

What are V-Play Plugins

Do you want to add advertisements to your app or game? Do you want to add analytics to track and understand your audience? Do you need push notifications or other cloud services?

V-Play got you covered! With V-Play Plugins, you can add such native cross-platform plugins with just a few lines of code.

 

v-play-native-cross-platform-plugins

 

Beginning with V-Play 2.14.2, you can use many of those plugins also with V-Play Live. This helps you test a plugin without performing any extra setup steps. V-Play Live has several plugins configured already, so you can use them right away. For some plugins, this is not possible yet.

Use V-Play Plugins with V-Play Live

Here is a short video showing how to use V-Play Plugins with V-Play Live:

 

The following list shows which plugins you can already use with V-Play Live. There are no extra setup steps needed. For some services you will need to create an account at the service provider website. You can find the website links in the code examples below.

Access V-Play Plugins

To use V-Play Plugins in your QML code, add

import VPlayPlugins 1.0

at the top of your QML file.

Google AdMob

The AdMob plugin offers several ad formats to optimize your monetization. It features banner ads, interstitial ads and rewarded videos.

AdMobBanner {
  // this is a public test Id, you can create your own at www.google.com/ads/admob/
  adUnitId: "ca-app-pub-3940256099942544/6300978111"
}

You can test the code examples in this post like this:

import QtQuick 2.0
import VPlayApps 1.0
import VPlayPlugins 1.0

App {
  NavigationStack {
    Page {
      title: "AdMob"

      AdMobBanner {
        adUnitId: "ca-app-pub-3940256099942544/6300978111"
      }
    }
  }
}

Chartboost

Charboost is another advertisement service that offers interstitial ads. You can also use it to cross-promote your apps & games.

Chartboost {
  appId: "your App Id from www.chartboost.com"
  appSignature: "your App Signature from www.chartboost.com"
  shouldRequestInterstitialsInFirstSession: true
 
  onPluginLoaded: {
    cacheInterstitial()
  }
  onInterstitialCached: {
    chartboost.showInterstitial()
  }
  onInterstitialFailedToLoad: {
    console.debug("InterstitialFailedToLoad at location:", location, "error:", error)
  }
}

Google Analytics

Google Analytics offers in-depth analysis how users interact with your apps & games.

GoogleAnalytics {
  propertyId: "your Property Id from analytics.google.com"
  
  onPluginLoaded: {
    logEvent("V-Play Live","Test App Loaded","Test Events", 1)
  }
}

Flurry Analytics

Flurry is another popular analytics service that’s provided as V-Play Plugin. Here is a short example how to use it:

Flurry {
  id: flurry
  apiKey: "your Api Key from y.flurry.com"

  onPluginLoaded: {
    flurry.logEvent("App started");
  }
}

V-Play Game Network & Cloud Services

V-Play Game Network offers cross-platform leaderboards and achievements. You can also use an integrated cloud storage for your apps & games.

You can find more about the offered services here: V-Play Game Network & Cloud Services

V-Play Multiplayer & Social Services

V-Play Multiplayer enables you to create real-time and round-based multiplayer games. You can also use those features in your apps, and connect your users with the integrated chat system.

You can read more about the offered services here: V-Play Multiplayer & Social Services

Local Push Notifications

Schedule local notifications based on an upcoming point in time or a time interval.

This code examples schedules a notification 5 seconds after pressing the button. If you close the app, or move it to the background, the notification will be received as push notification.

NotificationManager {
  id: notificationManager
  onNotificationFired: {
    // Log output when the notification is fired. You can find the log output in the Live Server
    console.debug("Notification fired: " + notificationId)
  }
}

Notification {
  id: testNotification
  notificationId: "testNotification"
  message: "Hey there!"
  timeInterval: 5 // in seconds
}

AppButton {
  text: "Schedule Notification"
  onClicked: {
    // Cancel old notification if scheduled
    notificationManager.cancelNotification(testNotification.notificationId)
    // Schedule testNotification
    notificationManager.scheduleNotification(testNotification)
  }
}

More Plugins

You can find the full list of V-Play Plugins here: V-Play Plugins

The following plugins will have no or limited functionality with V-Play Live at the moment: In-App purchases, Facebook, Google Firebase, Game Center, Google Cloud Messaging, OneSignal, HockeyApp.

Of course, you can still use those plugins with standard local deployment, as used to. Nothing changes for your existing projects.

SocialView Improvements

This update brings many new features for the SocialView. For example, you can now search users and start new chats with the newMessagePage. It is even possible to create a custom user search with the SocialUserSearchPage base type.

business-meet-and-chat

For a guide how to best use the SocialView in your projects, please see How to Add In-App Chat or Gamification Features to Your Mobile App.

Navigation Fix

The update also brings a fix for triggering NavigationItem signals like onSelected.

Changelog

For a full list of improvements and fixes to V-Play in this update, please check out the change log!

 

How to Update V-Play

Test out these new features by following these steps:

  • Open the V-Play SDK Maintenance Tool in your V-Play SDK directory.
  • Choose “Update components” and finish the update process to get this release as described in the V-Play Update Guide.

V-Play Update in Maintenance Tool

If you haven’t installed V-Play yet, you can do so now with the latest installer from here. Now you can explore all of the new features included in this release!

 

 

 

More Posts Like This

v-play-2-14-1-live-reloading-windows-mac-linux-ios-android-qt

Release 2.14.1: Update to Qt 5.9.3 | Use Live Code Reloading on macOS and Linux

v-play-live-reloading-windows-mac-linux-ios-android-qt

Release 2.14.0: Live Code Reloading for Desktop, iOS & Android

 

feature

How to Make Cross-Platform Mobile Apps with Qt – V-Play Apps

The post Release 2.14.2: Live Code Reloading with Native Cross-Platform Plugins appeared first on V-Play Engine.

Jump-Start Your Development with the Qt Starter Pack

If you want to impress today’s customers, you have to keep up with the slick and smooth UIs they’re used to from their smartphones and tablets. Fortunately, the Qt software framework is no slouch and can fulfill the highest product requirements for embedded and desktop solutions.

Driving the Qt technology and its road map, we’re very proud of how comprehensive the Qt framework is. However, the sheer amount of the libraries can seem a bit daunting for Qt developers, some of which are still pondering how to use them to their full potential. To help you with this challenge right out of the gate and save you time and money, we have created a new globally available service: The Qt Starter Pack.

What is The Qt Starter Pack?

The Qt Starter Pack consists of an on-site workshop with a dedicated Qt engineering specialist with additional consulting after the workshop. Our expert developers teach you how to move things forward quickly at the start of the project, reaching your project milestones and go-to-market in record time.

Sounds great coming from us, but here is what some of our customers had to say:

The Qt Starter Pack

The Qt consultants gave us initial training which really helped us kick start our project & avoid typical software development pitfalls enabling us to get to market faster.” Kristof Braem, CEO, Medec Benelux

With the support of Qt consultants we were able to leverage on their knowledge and create one of the most advanced In-Vehicle Infotainment (IVI) systems and instrument clusters on the market – from scratch.” Goran Haček, IVI development coordinator, Rimac Automobili

It was quite amazing for us to see that we were able to cut down the development time from 1000 – 1300 hrs to 50 hrs by being able to allow all developers and customers to work on the same IDE.” Rune Volden, R&D Manager, Ulstein Power & Control

Sounds interesting, tell me more!

A dedicated Qt specialist takes you on a deep dive into product design and development and sets the solution development with Qt on the right path.

Of course, we tailor the workshop to your specific needs, but here are some general topics you can expect.

  • Use cases walk-through and feasibility studies
  • Hardware evaluation and optimization best practices
  • Evaluation of different options for high-level architecture design
  • Implementation practices
  • Memory footprint configuration and optimization
  • Identification of possible risks and bottlenecks with recommendations
  • Covering your specific topics and questions

With our experience in product development, we will help you avoid common pitfalls, manage performance and memory consumption and build a solid foundation for your project.

How can I get it?

We are happy to tell you more about the Qt Starter Pack and how we can help you to succeed. Contact us to discuss how we can boost your development to the next level.

The post Jump-Start Your Development with the Qt Starter Pack appeared first on Qt Blog.

SDDM v0.17.0

We are happy to release a new SDDM with a few improvements.

SDDM is a Qt based graphical login manager developed in a collaborative fashion by people from Liri, KDE and LXQt.

This new version include the following changes:

* Added possibility to change border width of ComboBox widget.
* Added missing utmp/wtmp/btmp handling.
* Make greeter see icons set by AccountsServices.
* Fix sddm.conf man page syntax error and update.
* Fix ComboBox widget.
* Fix connection of PropertiesChanged event for LogindSeat.
* Avoid race conditions with Xsetup.
* Update de translation.
* Update lt translation.
* Update zh_TW translation.
* Adjust order of components in the default PATH.
* Set default input method to qtvirtualkeyboard.


Download it here.

Increasing the number of lights in Qt 3D

While it is possible to draw scenes with almost unlimited numbers of lights using deferred rendering in Qt 3D, the default materials in Qt 3D Extras have been limited to eight lights because they are tied to forward rendering. Although we apply a few tricks that allow you to define more than eight lights in the scene, we only select the eight closest lights when rendering.

In the scene below, we have defined 100 lights that float around, but because only 8 of them are in use at the time and you can see how they pop in and out:

Although it’s possible to support more lights in other pipelines (this is for instance the case with the Qt 3D Studio Runtime), we also want the default materials in Qt 3D Extras to be able to use a larger number of lights, even when used with forward rendering.

The reason behind the current limit is that we need to support a range of OpenGL architectures, including some which are quite limited in terms of how much uniform data can be passed to the shader. We therefore need to pass a small array of light structs to the shader and loop over the lights in the fragment shader. Unfortunately, such loops are also very expensive on hardware with limited resources because they compile to a large number of shader program instructions. Sometimes so many that the resulting shader program won’t compile at all.

We have therefore started work on a solution that will both allow us to increase the number of lights on beefy hardware and at the same time reduce the number of shader program instructions on weaker hardware. It is based on Qt 3D’s new internal shader graph, which was recently introduced by Kevin Ottens from KDAB.

The shader graph is a game changer for Qt 3D. It allows us to build shaders dynamically with a structure similar to the material graphs you find in many modern 3D modeling applications. Each node in the graph can be thought of as a function – it is defined as a computation, takes a set of input values, and produces output values. For instance, a surface node can take the incoming and outgoing angles of light, the light color, and the surface color, and compute the resulting light contribution. By connecting nodes in the graph, you define the flow of computations in the shader program, which is translated to instructions in a programming language. Because all the computations are abstract, they can easily be translated to many different shader languages (or dialects), making it trivial to produce the same material definitions for different architectures.

In terms of increasing the number of lights, the great thing about the shader graph is that we can easily define a shader node for each of the lights in the scene. The graph then results in the right number of light calculations when converted to shader code. Instead of having a for loop inside the shader itself, we loop over the lights when the shader is generated. This is basically unrolling the old loop, which is way more efficient and allows us to have a much higher number of lights.

I am currently working on a prototype where we have moved the lighting system on top of the shader graph. While this work is still highly experimental, it is already producing some promising results. Here’s the same scene as above, but this time we are actually seeing 100 lights in use at the same time:

Before this becomes part of the default materials in Qt 3D Extras, we also want to change how lights are gathered in a scene and passed on to the shaders. Currently, the light gathering is tightly coupled with the rendering code, but we want to reduce the number of assumptions we make and give you more control over how light information is passed to your shaders. In turn, this will also make it easier to create applications that can switch between rendering modes, for instance if you want to support both forward and deferred rendering of the same scene

If this sounds interesting for your applications, feel free to share thoughts on how you would like to work with lights in your code.

The post Increasing the number of lights in Qt 3D appeared first on Qt Blog.

Qt 5.10 Rendering Benchmarks

Qt 5.10.0 RC packages are available now and actual release is happening pretty soon. So this seems to be a good time to run some rendering benchmarks with 5.10, including new QML Shape element, QQuickPaintedItem and QNanoPainter.

After my previous blog post, some initial comments mentioned how QML Shape didn't reach their performance expectations. But I think that might be more of a "use the right tool for the job" -kind of thing. This demo application is very much designed to test the limits of how much heavily animated graphics can be drawn while keeping performance high and while having its own strengths, QML Shape likely isn't the tool for that.

To prove this point, there is a new 'flower' test case in QNanoPainter demo app which renders a nice flower path, animating gradient color & rotation (but not path). Combining it with new setting to render multiple items (not just multiple renders per item) and the outcome looks like this with 1 and 16 items:


Now when we know what the desired outcome looks like let's start testing with the first run. 

Test1: Nexus 6, 'Render flower' test:


Test1 conclusions: In this test QQuickPaintedItem (QImage backend) has clearly worst performance, CPU Raster paint engine and uploading into GPU is very non-optimal on Nexus 6. QML Shape performs the best, maintaining fluid 60fps still with 16 individual items. QNanoPainter manages quite well also and switching for QSGRenderNode backend instead of QQuickFramebufferObject to avoid rendering going through FBO gives a nice boost. When the amount of items increases this FBO overhead naturally also increases. QQuickPaintedItem with FBO backend is somewhat slower than QNanoPainter.

This test is kind of best-case-scenario for QML Shape. If path would animate that would be costly for QML Shape backend. Also for example enabling antialiasing turns tables, making QML Shape only render 2 items at 35fps while QNanoPainter manages fluid antialiased 60fps. But that's the thing, select the proper tool for your use case.

Next we can test more complex rendering where also paths animate and see how antialiasing affects the performance. In rest of the tests, instead of increasing item count we increase rendering count, meaning how many times stuff is rendered into a single QQuickItem. The default tests set contains ruler, circles, bezier lines, bars, and icons+text tests. With 1, 2 and 16 rendering counts it looks like this:



So let's continue to Test2: Nexus 6, all default tests enabled:


Test2 conclusions: Slowest performer is again QQuickPaintedItem (QImage). QML Shape becomes right after it, dropping quite a bit from lead position of Test1. Digging QML Shape performance a bit deeper and enabling different tests individually one can see that Bezier lines test makes the biggest fps hit. And disabling some code there revealed that biggest slowdown came from graph dots which were drawn with two PathArc, so improved fps by switching implementation to use QML Rectangle instead. QNanoPainter is fastest but even it only reaches 60fps with non antialiased single rendering. Note that QNanoPainter with QSGRenderNode is missing here and in all rest of the tests because when rendering only single item performance of it is almost the same as QNanoPainter with FBO.

Then we could switch to a bit more powerful hardware and repeat above test with that. 

Test3: Macbook Pro (Mid 2015, AMD R9 M370X), all default tests enabled:


Test3 conclusions: Macbook can clearly handle much more rendering than Nexus 6. As MSAA is fully supported here we are able to test both antialiased and non-antialiased for every rendering method. On macbook MSAA antialiasing is quite cheap which can be seen from QML Shape and QQuickPaintedItem reaching pretty similar frame rates with and without antialiasing. Slowest performer is antialiased QQuickPaintedItem (QImage) while QNanoPainter leading again, reaching solid 60fps with 16 render counts.

As we saw already earlier that Bezier lines test seemed particularly unsuitable for QML Shape, let's next repeat the above test except disabling that single test. After all we try to be fair here and avoid misinterpretations. 

Test4: Macbook Pro, all default tests except Bezier lines enabled:


Test4 conclusions: Most interesting data here comes from comparison to Test3 results. QQuickPaintedItem (QImage) results go up only few percentages, so bezier line test doesn't seem to influence much there. QQuickPaintedItem (FBO) results are now identical for antialiased and non antialiased so light blue line can't be seen under orange one. But not much changes in there either. QNanoPainter improves 30-50% reaching solid 60fps now with 32 render counts when antialiasing is disabled. And finally, QML Shape improves frame rates by whopping ~100% so we were right in this particular test being its Achilles' heel.

We are just scratching surface here. There would be plenty of things to test still and get deeper into individual tests. But for this blog post let's stop here.

General tips about about Qt 5.10 QML Shape usage could be:
  • Use QML Shape for simple shape items as part of QML UIs. Consider other options for more complex shapes which animate also the path. 
  • Also don't use non-trivial Shape elements in places where creation time matters e.g. ListView delegates or making multiple shapes inside Repeater, as parsing the QML into renderable nodes tree has some overhead.
  • When the need is to render rectangles, straight lines or circles, QML Rectangle element gives generally better performance than QML Shape counterpart. You can experiment with this enabling alternative code paths for RulerComponent and LinesComponent of the demo. 
  • If you target mostly hardware with NVIDIA GPU, GL_NV_path_rendering backend of QML Shape should be more performant. I didn't have suitable NVIDIA hardware available currently for testing so these results will have to wait, anyone else want to provide comparisons?

Follow up post is planned for comparing Windows side OpenGL vs. OpenGL ES + Angle rendering performances so stay tuned!

Sharing Files on Android or iOS from your Qt App

by Ekkehard Gentz [Independent Software Architect, Consultant] (Qt Blog)

It‘s a common usecase to share Files or Content from native Android or iOS Apps with other Apps on your phone. So I thought this would be an easy task to add sharing to my mobile Apps built with QtQuickControls2.

Found the Blog from Eskil Abrahamsen Blomfeld about Intents with Qt for Android, part 1. Please read that Blog to learn about Android Intents, Qt Android Extras, JNI and HowTo use it from Qt. All of this was new to me – never did JNI before. Also I‘m not an experienced native developer for Android or iOS – that‘s the reason why I‘m using Qt for mobile App development.

I also found Share on iOS and Android using QML, where I learned HowTo share Text and a URL and HowTo structure a QtCreator project for Android and iOS with native code integration.

Unfortunately all of this didn‘t help me to share Files on Android or iOS from Qt mobile Apps.

For a DropBox-like project at customer site I needed sharing:

  • download Files from cloud
  • store Files in App sandbox data
  • view or edit Files in other Apps
  • print Files to local Printer

It took me some time and thanks to much help from other developers at Qt Forum and Slack (QtMob) I found ways to solve my use-cases.

The hardest part was the ugly JNI and Obj-C syntax 😉

Hopefully my experiences will make it easier for others to integrate Sharing-functionality into their Android or iOS Apps.

Here‘s my Example App at Github.

The Example App demonstrates:

  • Share Text and URL
  • View File
  • Edit File
  • Send File as Stream to …

To easy understand the complex structure please take a look at this Overview:

share_overview

UI: SwipeView with some Tabs. Each Tab contains a Page with some Buttons to test features.

01_tab_bar

Share Text and URL is the easiest one:

02_android_share_text_url

ShareUtils is registered as „shareUtils“:

void ApplicationUI::addContextProperty(QQmlContext *context)
{
    context->setContextProperty("shareUtils", mShareUtils);
}

Click on the Button to share Text and URL:

Button {
    text: qsTr("Share Text and Url")
    onClicked: {
        shareUtils.share("Qt","http://qt.io")
    }
}

Here‘s the share() method:

Q_INVOKABLE void share(const QString &text, const QUrl &url);
void ShareUtils::share(const QString &text, const QUrl &url)
{
    mPlatformShareUtils->share(text, url);
}

ShareUtils delegates this to PlatformShareUtils:

  • AndroidShareUtils on Android
  • IosShareUtils on iOS

The magic to detect the right class can be found in ShareUtils:

#if defined(Q_OS_IOS)
    mPlatformShareUtils = new IosShareUtils(this);
#elif defined(Q_OS_ANDROID)
    mPlatformShareUtils = new AndroidShareUtils(this);
#else
    mPlatformShareUtils = new PlatformShareUtils(this);
#endif

The Compiler knows the Platform you‘re running on and instantiates the matching platform specificClass.

Android:

void AndroidShareUtils::share(const QString &text, const QUrl &url)
{
    QAndroidJniObject jsText = QAndroidJniObject::fromString(text);
    QAndroidJniObject jsUrl = QAndroidJniObject::fromString(url.toString());
    jboolean ok = QAndroidJniObject::callStaticMethod("org/ekkescorner/utils/QShareUtils",
                                              "share",
                                              "(Ljava/lang/String;Ljava/lang/String;)Z",
                                              jsText.object(), jsUrl.object());
    if(!ok) {
        emit shareNoAppAvailable(0);
    }
}

QAndroidJniObjects are created for text and url and a Method from QShareUtils.java will be called:

    public static boolean share(String text, String url) {
        if (QtNative.activity() == null)
            return false;
        Intent sendIntent = new Intent();
        sendIntent.setAction(Intent.ACTION_SEND);
        sendIntent.putExtra(Intent.EXTRA_TEXT, text + " " + url);
        sendIntent.setType("text/plain");
        // Verify that the intent will resolve to an activity
        if (sendIntent.resolveActivity(QtNative.activity().getPackageManager()) != null) {
            QtNative.activity().startActivity(sendIntent);
            return true;
        } else {
            Log.d("ekkescorner share", "Intent not resolved");
        }
        return false;
    }

In our QShareUtils.java we create the Intent with ACTION_SEND and EXTRA_TEXT and start the Activity using QtNative.activity().

It‘s important to test if the Intent can be resolved – otherwise your App will crash.

Here‘s the iOS implementation:

void IosShareUtils::share(const QString &text, const QUrl &url) {
    NSMutableArray *sharingItems = [NSMutableArray new];
    if (!text.isEmpty()) {
        [sharingItems addObject:text.toNSString()];
    }
    if (url.isValid()) {
        [sharingItems addObject:url.toNSURL()];
    }
    // get the main window rootViewController
    UIViewController *qtUIViewController = [[UIApplication sharedApplication].keyWindow rootViewController];
    UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:sharingItems applicationActivities:nil];
    if ( [activityController respondsToSelector:@selector(popoverPresentationController)] ) { // iOS8
        activityController.popoverPresentationController.sourceView = qtUIViewController.view;
    }
    [qtUIViewController presentViewController:activityController animated:YES completion:nil];
}

In this case we‘re using UIActivityViewController to get the share options displayed.

Let‘s take a look HowTo view or edit Files. My Example App contains an Image and a PDF File in assets:

03_assets

At first start of the Example App we copy these files from Assets to (QstandardPaths::AppDataLocation)/my_share_files. This simulates that you have downloaded the Files from your Cloud or WebService.

See all the details in the sources.

Now we want to view or edit these Files in another App. I learned that it‘s not possible to share Files from your AppData Location (Sandbox) – you must copy the Files from APP Data to USER Data – per ex. Documents Location. I create a app specific working directory at DocumentsLocation.

Attention: I‘m not checking if Permissions are set – this isn‘t the goal of this App. To access Files at DocumentLocation you have to set WRITE_EXTERNAL_STORAGE.

<uses-permission android:name=”android.permission.WRITE_EXTERNAL_STORAGE” />

Using Files from DocumentsLocation sounds easy but makes things more complicated. We don‘t want to leave those Files at DocumentsLocation when sharing the File finished. Then we must delete them. And if the User edited the File we must know if the File was changed and saved or canceled. If the File was modified we must replace the origin File inside our AppData before deleting the File from DocumentsLocation.

Also after sending the File as a Stream per ex. to a Printer we must delete the copied File as soon as printing was done.

The workflows are different for Android and iOS.

Android needs

  • FilePath URI
  • a specific Action: ACTION_VIEW, ACTION_EDIT, ACTION_SEND
  • MimeType

to find matching Apps.

iOS needs

  • FilePath as NSURL

On iOS there‘s no way to limit the Apps for View or Edit capabilities.

Android does an in-place-Editing of the File, where iOS copies the File over to the other App. To get the modified File back a second manual step is required by the user.

file_flow

The interesting question now is „HowTo know when sharing is finished and if Edit mode was canceled or saved“ ?

Android gives us the startActivityForResult() to get a Result Code back.

Unfortunately I didn‘t found a way to get this Result Code back from the Java Code in QShareUtils.java.

Using Java code on the other side is much easier to read and write as JNI code.

There‘s a workaround: as soon as the Intent Activity is started, our Qt App changes ApplicationState to Suspended and when the Intent Activity closes, the ApplicationState goes back to Active. You can watch the ApplicationState to know when sharing was finished and you also can compare modified Timestamp of the File to recognize changes from Edit mode.

This workflow is marked ‚A‘ in the Overview at the beginning of this article.

More coding, but much more flexible is to use JNI to construct the complete Intent Activity and to go with QAndroidActivityResultReceiver To get the Result back.

Here‘s the method to construct the Edit Intent:

    QAndroidJniObject jniPath = QAndroidJniObject::fromString("file://"+filePath);
    if(!jniPath.isValid()) {
        emit shareError(requestId, tr("Share: an Error occured\nFilePath not valid"));
        return;
    }
    // next step: convert filePath Java String into Java Uri
    QAndroidJniObject jniUri = QAndroidJniObject::callStaticObjectMethod("android/net/Uri", "parse", "(Ljava/lang/String;)Landroid/net/Uri;", jniPath.object());
    if(!jniUri.isValid()) {
        emit shareError(requestId, tr("Share: an Error occured\nURI not valid"));
        return;
    }
    // THE INTENT ACTION
    // create a Java String for the ACTION
    QAndroidJniObject jniParam = QAndroidJniObject::getStaticObjectField("android/content/Intent", "ACTION_EDIT");
    if(!jniParam.isValid()) {
        emit shareError(requestId, tr("Share: an Error occured"));
        return;
    }
    // then create the Intent Object for this Action
    QAndroidJniObject jniIntent("android/content/Intent","(Ljava/lang/String;)V",jniParam.object());
    if(!jniIntent.isValid()) {
        emit shareError(requestId, tr("Share: an Error occured"));
        return;
    }
    // THE FILE TYPE
    if(mimeType.isEmpty()) {
        emit shareError(requestId, tr("Share: an Error occured\nMimeType is empty"));
        return;
    }
    // create a Java String for the File Type (Mime Type)
    QAndroidJniObject jniType = QAndroidJniObject::fromString(mimeType);
    if(!jniType.isValid()) {
        emit shareError(requestId, tr("Share: an Error occured\nMimeType not valid"));
        return;
    }
    // set Data (the URI) and Type (MimeType)
    QAndroidJniObject jniResult = jniIntent.callObjectMethod("setDataAndType", "(Landroid/net/Uri;Ljava/lang/String;)Landroid/content/Intent;", jniUri.object(), jniType.object());
    if(!jniResult.isValid()) {
        emit shareError(requestId, tr("Share: an Error occured"));
        return;
    }
    QAndroidJniObject activity = QtAndroid::androidActivity();
    QAndroidJniObject packageManager = activity.callObjectMethod("getPackageManager",
                                                                 "()Landroid/content/pm/PackageManager;");
    QAndroidJniObject componentName = jniIntent.callObjectMethod("resolveActivity",
                                                              "(Landroid/content/pm/PackageManager;)Landroid/content/ComponentName;",
                                                              packageManager.object());
    if (!componentName.isValid()) {
        emit shareNoAppAvailable(requestId);
        return;
    }
    // now all is ready to start the Activity:
    // we have the JNI Object, know the requestId
    // and want the Result back into 'this' handleActivityResult(...)
    QtAndroid::startActivity(jniIntent, requestId, this);

Thanks to QAndroidActivityResultReceiver we‘ll get the Result back into:

void AndroidShareUtils::handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data)
{
    Q_UNUSED(data);
    // we're getting RESULT_OK only if edit is done
    if(resultCode == RESULT_OK) {
        emit shareEditDone(receiverRequestCode);
    } else if(resultCode == RESULT_CANCELED) {
        emit shareFinished(receiverRequestCode);
    } else {
        emit shareError(receiverRequestCode, tr("Share: an Error occured"));
    }
}

Now it‘s easy to connect to the SIGNAL (shareEditDone, shareFinished, shareError) from QML.This workflow is marked as ‚B‘ in the Overview above.

Please note: the JNI code is different for VIEW/EDIT and SEND – please take a look at all the details in the sources.

Was not so easy to figure out all the details but now it‘s really easy for you: just copy the code into your app 🙂

On iOS we‘re using a UIDocumentInteractionController:

    NSString* nsFilePath = filePath.toNSString();
    NSURL *nsFileUrl = [NSURL fileURLWithPath:nsFilePath];
    static DocViewController* docViewController = nil;
    if(docViewController!=nil)
    {
        [docViewController removeFromParentViewController];
        [docViewController release];
    }
    UIDocumentInteractionController* documentInteractionController = nil;
    documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:nsFileUrl];
    UIViewController* qtUIViewController = [[[[UIApplication sharedApplication]windows] firstObject]rootViewController];
    if(qtUIViewController!=nil)
    {
        docViewController = [[DocViewController alloc] init];
        docViewController.requestId = requestId;
        // we need this to be able to execute handleDocumentPreviewDone() method,
        // when preview was finished
        docViewController.mIosShareUtils = this;
        [qtUIViewController addChildViewController:docViewController];
        documentInteractionController.delegate = docViewController;
        [documentInteractionController presentPreviewAnimated:YES];
    }

Know when Preview is done from DocViewController:

-
(void)documentInteractionControllerDidEndPreview:(UIDocumentInteractionController *)controller
{
#pragma unused (controller)
    self.mIosShareUtils->handleDocumentPreviewDone(self.requestId);
    [self removeFromParentViewController];
}

We detect the end of preview and call documentPreviewDone() in IosShareUtils:

void IosShareUtils::handleDocumentPreviewDone(const int &requestId)
{
    emit shareFinished(requestId);
}

Now from QML we can connect to the SIGNAL and know when sharing was done.

The good thing: the workflow now is similar for Android and iOS and easier to handle from common QML code.

Please take a look at .pro – there are some Android and iOS specific sections. Also dont forget to add AndroidExtras.

Now it‘s time to download from Github, build and run the Sharing Example App.

Some Screenshots

Open in… Dialog on Android. Select the App you want to use for View or Edit mode:

04_android_share_chooser

Edit the Image and cancel or save:

05_android_share_edit_image

ACTION_SEND enables you to print Text, Images or PDF Files.

Choose the Printer Plugin:

06_android_share_send_chooser

Then select your Printer, configure the Printer and print:

07_android_share_print

On iOS the UIDocumentInteractionController gives you a preview of the File and provides the iOS Share Button at bottom left:

08_ios_preview

The Share Button presents the available Apps:

09_ios_share

Conclusion: you can share Files on Android and iOS and integrate into your specific workflows. SIGNALS are mitted to let you know what the User has done.

There‘s even a call to check if for a given MimeType and Action Type any matching Apps are available on Android.

Call this check before downloading a File from Cloud, to provide a better UX: instead of downloading and then telling users that there‘s no App available you can inform in advance.

One question is open for now: HowTo suppress the Preview on iOS and only show what you get from clicking on the Share Button. Thanks to any tips from Qt / Obj-C experts.

I‘ll continue work on this app and add some more Intent Actions for Android.Also will add functionality to open the App from outside by providing a File URI or MimeType.

Now have fun!

The post Sharing Files on Android or iOS from your Qt App appeared first on Qt Blog.

Release 2.14.1: Update to Qt 5.9.3 | Use Live Code Reloading on macOS and Linux

V-Play 2.14.1 adds Live Code Reloading Support for macOS & Linux Desktop. With this addition, Live Code Reloading now allows to run and reload your projects on iOS and Android from all desktop platforms. No native iOS and Android build tools required.

V-Play 2.14.1 also introduces the new SocialView component for adding social features like user profile, leaderboard or even a full-featured messenger to your apps and games. It offers a cleaner and more customizable interface, compared to the more game-oriented VPlayGameNetworkView and VPlayMultiplayerView.

This update also adds support for Qt 5.9.3.

Live Code Reloading for Windows, macOS and Linux

Building your project takes some time. For mobile platforms, it can even take several minutes, depending on your development PC. This can become quite time-consuming. For example, if you are doing small iterations to fine-tune UI elements in apps. Same with animations, game-mechanics and so on. It slows down your development speed and therefore decreases your productivity.

V-Play Live reloads your project on every connected device, within seconds after code changes. This reduces the long compilation and deployment times, to only a couple of seconds. V-Play Live also allows you to run and test your applications on iOS from a Windows or Linux desktop. You don’t need to install any native SDKs for deployment with V-Play Live. See here for a quick demo:

 

This video shows a project opened in Qt Creator (left) on Windows. The project runs with V-Play Live. In the bottom right corner, the screens from an Android Nexus 7 and an iPhone 5s are shared with screen mirror tools so you can see what is going on there. In the center, you can see the desktop client. After connecting the 2 devices to the Live Server (top right), we change the code in Qt creator. After we save the changes, all connected clients update their view, within a second!

Qt Charts with V-Play Live

After some of you requested it, this update also contains the powerful Qt Charts module with V-Play Live. Including charts in your app could not be easier.

V-Play Live Reload Benefits

Save Time: Deploy and Test in Seconds

V-Play Live reduces deployment time, from several minutes to a couple of seconds. It deploys your code changes to all connected devices, automatically after saving.

Let’s play a bit with numbers. If you have a really decent development PC, you might be able to deploy your application to Android within one minute. With V-Play Live, it will take 1-2 seconds.

1 - newTime/oldTime = 1 - 2/60 = 0.9667

You just saved 96.67% of time for deployment. If you deploy only 10 times per hour, you spent 20 seconds deploying, instead of 10 minutes. That is 2 extra coffee breaks per hour. Or more time to spend productive.

In this demo, you see all connected clients (Desktop, Android and iOS) update within a second after clicking save in the development environment:

v-play-live-code-change-reload-windows-android-ios

Feel free to do the calculation with numbers that are more realistic in your own case.

More V-Play Live Benefits

You can find more benefits in last week’s blog post.

How to Use V-Play Live

1. Get the Latest Version of V-Play

If you haven’t installed V-Play yet, go get it on our download page. If you already have V-Play installed, see the update instructions below.

2. Start V-Play Live

To start V-Play Live for your active project, click on the LIVE-Run button in Qt Creator:

v-play-live-run-button

The V-Play Live Server (below right) will start, and open a local V-Play Live Client (below left).

vplay-live-server-and-client-ui

The Live Server allows you to:

  • Manage your connected clients, both desktop and mobile.
  • See the log output of each connected client in a separate tab.
  • Detect code changes and reload all connected clients.
  • Select the qml file you would like to run. The default is the main entry point.

3. Connect Live Clients

If you do not want to connect any mobile device now, you are already set and can start development with your local Client. To connect a mobile device, download the V-Play Live App, for Android or iOS.

Google_Play_Badge-1App Store
ic_launcherV-Play & QML Live Scripting App
Search in App Stores for: “V-Play Live Scripting”

V-Play Live uses your local network to communicate between server and clients. Make sure that your device is connected to the same network as your development PC running the Live Server, for example the same Wi-Fi. The V-Play Live App will try connecting to the server once you run it on your mobile device. The Live Server then displays this dialog for the connection request:

vplay-live-client-connection-requesti

Accept the mobile device connection on the Server. Now all code changes are automatically sent to your connected mobile device(s).

Add Social Features to your Apps with the SocialView

Custom social features in your app boost retention and engagement rates. Many successful apps allow users to interact, compete or build social relationships. This is because social features are one of the key drivers to build up your user base.

The V-Play Game Network and V-Play Multiplayer & Messaging services include many ready-made social services. This makes it easy for you to create apps with e.g. user profiles, leaderboards or a messaging and friend system. This year’s conference management app for the Qt World Summit 2017 uses these services and allows to:

  • Search conference attendees to schedule Business Meetings.
  • Store custom user data like the company, job position or main Qt interest.
  • View user details and find conference attendees that match certain criteria.
  • Log in via Facebook to synchronize your data across devices.
  • Become friends and get in touch with the included messenger.
  • See a leaderboard with a ranking of users that work with the app, favor many talks and are active.

business-meet-and-chat

You can have a look and try out the features by downloading the app for iOS or Android:

App Store Google_Play_Badge-1

A cloud-based backend and easy-to-use client QML components allow to use these features. The VPlayGameNetworkView and VPlayMultiplayerView provide a default UI for your V-Play Apps. These types include several child views you can show or hide using view states. But there are several drawbacks to this approach. The new SocialView type completely changes the way you can use social services in your apps or games:

  • V-Play Apps Compatibility: The SocialView integrates with your App Navigation. You can show the available view pages anywhere in your app!
  • Easier Customization: Extend the view with QML elements to weave in your custom user data. Touching the QML source code of the view is not required.
  • Native Look and Feel: The default UI of the new view uses a native style and user experience on iOS and Android.
  • Clean Separation into App Pages: Each page instance works standalone and is a unique view. This also allows to introduce custom social pages to work with the V-Play social services.

mobile-chat

The Qt World Summit 2017 app shows how to use the SocialView and enriches it with custom features. The full updated source code is available for you on GitHub.

Dynamic App Navigation for Android and iOS

You don’t have to worry about how to show a drawer menu on Android, and a tab navigation on iOS. The V-Play App Navigation components take care of providing a native main menu.

But you also want change the general menu setup in some cases. For example to:

  • Only make certain features visible based on specific conditions.
  • Add more quick-links to your Android menu, as there’s more available space in the drawer than in the iOS tab bar.
  • Switch between different configurations of a certain menu item.

You can now set conditions for showing a menu item with NavigationItem::showItem. This allows to hide and show items in your main navigation without a hassle. For example, it is now easy to show different navigation entries for iOS and Android:

NavigationItem {
  title: "Only on iOS"
  icon: IconType.apple
  showItem: Theme.isIos

  // ...
}

 

Besides the highlights mentioned above, there are many other fixes and improvements for the SDK. For a full list of improvements and fixes to V-Play in this update, please check out our change log!

How to Update V-Play

Test out these new features by following these steps:

  • Open the V-Play SDK Maintenance Tool in your V-Play SDK directory.
  • Choose “Update components” and finish the update process to get this release as described in the V-Play Update Guide.

V-Play Update in Maintenance Tool

If you haven’t installed V-Play yet, you can do so now with the latest installer from here. Now you can explore all of the new features included in this release!

 

 

More Posts Like This

v-play-live-reloading-windows-mac-linux-ios-android-qt

Release 2.14.0: Live Code Reloading for Desktop, iOS & Android

How to Make Cross-Platform Mobile Apps with Qt – V-Play Apps

How to Make a Qt app

The post Release 2.14.1: Update to Qt 5.9.3 | Use Live Code Reloading on macOS and Linux appeared first on V-Play Engine.