QML: Mixed native Android and QML animated splash screen


In this blog two methods for show a splash screen has been proposed. One is based to pure QML code and show animations and the second is native Android way but static. Now a third method mixing both previous solution by allow a partially animated splash screen is proposed.



Full project example can be found here.

Before proceed you must read the following posts for get details about each methods work:

      QML: Show Android native splash screen in your QML app
      QML: Show animated splash screen at startup

The idea here is to mix these methods by use the native Android way on this first phase and the second pure QML to show an animation on the second phase. The first phase is when the Android app is started. Here the Qt/QML engine not have control of the window yet and this mean it can not paint anything. By default you can see the standard Android template with the app icon on top right and the name of the app in a, usually, dark background. By using the native method way in this phase is possible to show a simple static splash screen by composing the graphic elements using the Android xml schema. Second phase is when Qt/QML engine take control of the app window. Here the thick is to paint an exact copy of the Android native splash screen plus an animated control informing the user the app is loading. In this case the pure QML splash screen method is used with the difference the image on the center have to be correctly sized by considering the screen DPI as native Android solution automatically do as follow:

Rectangle {
    id: splashRect
    color: "#81DAF5"

    Image {
        anchors.centerIn: parent
        source: "image://Android/icon"
        width: sourceSize.width / Screen.devicePixelRatio
        height: sourceSize.height / Screen.devicePixelRatio
    }

    BusyIndicator {
        id: busyAnimation
        anchors.horizontalCenter: parent.horizontalCenter
        anchors.bottom: parent.bottom
        anchors.bottomMargin: parent.height / 7
        width: parent.width / 3
        height: width
        running: true
    }
} 

A small trick here, for avoid duplicate resources, is to get the bitmap centered inside splash screen directly from Android resource system. Basically we use into QML the same image used by Android native splash screen. By make this step we need only to develop a QQuickImageProvider and "extract" the image from native resource system by using a bit of JNI as follow:

QImage AndroidImageProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
{
    QAndroidJniObject activity = QtAndroid::androidActivity();
    QAndroidJniObject resources, bitmap, packageName;
    int resourceID = 0;
    QImage Image;

    resources = activity.callObjectMethod("getResources", "()Landroid/content/res/Resources;");
    packageName = activity.callObjectMethod("getPackageName", "()Ljava/lang/String;");

    resourceID = resources.callMethod<jint>("getIdentifier",
                                            "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
                                            QAndroidJniObject::fromString(id).object<jstring>(),
                                            QAndroidJniObject::fromString("drawable").object<jstring>(),
                                            packageName.object<jstring>()
                                            );

    bitmap = QAndroidJniObject::callStaticObjectMethod("android/graphics/BitmapFactory",
                                                       "decodeResource",
                                                    "(Landroid/content/res/Resources;I)Landroid/graphics/Bitmap;",
                                                    resources.object(),
                                                    static_cast<jint>(resourceID)
                                                    );
    Image = AndroidBitmapToImage(bitmap);

    if(!Image.isNull())
    {
        *size = Image.size();
        if(requestedSize.isValid()) return Image.scaled(requestedSize);
    }

    return Image;
}

Please note, the function for convert Android bitmap format to Qt image format has been found in this great post of KDAB here. Another difference with the "original" QML splash screen method is the splash screen loader have to hide the native Android splash screen once finished load and show. This is the right time to "substitute" the native one with the animated version (red line is the difference):

Window {
    visible: true
    width: 600
    height: 800

    Loader {
        id: mainWindowLoader
        anchors.fill: parent
        active: false
        source: "qrc:/window.qml"
        asynchronous: true
        onLoaded: {
            item.visible = true;
            splashScreenLoader.item.visible = false;
            splashScreenLoader.source = "";
        }
    }

    Loader {
        id: splashScreenLoader
        source: "qrc:/splashscreen.qml"
        anchors.fill: parent
        onLoaded: {
            mainWindowLoader.active = true;
            android.hideSplashScreen();
        }
    }
}

Job done. Follow the screenshots of the two phases splash screen:

Native Android splash screen (phase 1)


QML animated splash screen (phase 2)

Comments

Popular posts from this blog

Access GPIO from Linux user space

Android: adb push and read-only file system error

Tree in SQL database: The Nested Set Model