Native Android and iOS code in Qt using status bar as an example

Hello! I am sure many have heard that Qt is very good for cross-platform mobile application development. However, to solve some problems, one has to deal with native code (Java, Objective-C), for example, calling a camera, gallery, calling a third-party api.


In this article, using a simple example of setting transparency for the status bar, I will show how to call native Java code and Objective-C.


whack


Andoid


The ability to use a transparent status bar appeared in Android 4.4 KitKat . In order for the status bar to become transparent, it is necessary to specify the transparency flag for Window in the Activity of our project (not to be confused with QQuickWindow , which is used to display QML).


If anyone does not know how to override their Activity from QtActivity

Open the Projects tab → Add assembly for Andoid → “Assembly” → Click “Details” in “Build Android APK” → “Create Templates”.


Thus, we created AndroidManifest, a folder with resources and gradle files, which will be located in the android folder. To place our java class, create the src folder in the android folder.


Create the file MyActivity.java . It is important that the path to the file matches the package name, i.e. using a package called com.example.myPackage, the path should be android / src / com / example / myPackage / MyActivity.java


package com.example.myPackage;
import org.qtproject.qt5.android.bindings.QtActivity;
import android.app.Activity;
import android.os.Bundle;
public class MyActivity extends QtActivity
{
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }
}

Now, we need to set the name of the Activity in AndroidManifest.xml . Are looking for


android:name="com.example.myPackage.MyActivity"

and change to


android:name="org.qtproject.qt5.android.bindings.QtActivity"

With these simple manipulations, we redefined the standard QtActivity.


The function that sets the transparency flag status bar and an example of its application:


@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setTranparentStatusBar();
}
void function setTranparentStatusBar() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
            getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
            getWindow().setStatusBarColor(Color.TRANSPARENT);
        } else {
            getWindow().setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,                         WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        }
}

Function returning the status bar height:


public int statusBarHeight() {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
        return 0;
    }
    int result = 0;
    int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
    if (resourceId > 0) {
        result = getResources().getDimensionPixelSize(resourceId);
    }
    return result;
}

In order to call the Java class method from QML, you will need to write a C ++ class that will use JNI . To work with JNI, add the Android module to our * .pro file :


QT += androidextras. 

Create a singleton class DeviceInfo.


DeviceInfo.h:


#pragma once
#include 
class DeviceInfo : public QObject
{
    Q_OBJECT
    Q_PROPERTY(int statusBarHeight READ statusBarHeight)
    public:
        DeviceInfo(QObject *parent = NULL);
        static DeviceInfo &instance(QObject *parent = 0);
        Q_INVOKABLE int statusBarHeight();
    private:
        static DeviceInfo _instance;
};

DeviceInfo.cpp:


#include "DeviceInfo.h"
#if defined(Q_OS_ANDROID)
    #include 
    #include 
    #include 
#endif
DeviceInfo::DeviceInfo(QObject *parent)
    : QObject(parent)
{}
DeviceInfo &DeviceInfo::instance(QObject *parent)
{
    static DeviceInfo instance(parent);
    return instance;
}
int DeviceInfo::statusBarHeight()
{
#if defined (Q_OS_ANDROID)
    QAndroidJniObject activity = QtAndroid::androidActivity();
    jint height = activity.callMethod("statusBarHeight");
    return (int) height;
#endif
    return 0;
}

Next, define our class in QML:


view.rootContext()->setContextProperty("DeviceInfo", &DeviceInfo::instance());

Everything is ready, it remains only to call the statusBarHeight method in QML:


Rectangle {
    width: parent.width
    height: DeviceInfo.statusBarHeight()
}

The result on the screen:


android screenshot


iOS


The ability to set a different style for the status bar in iOS appeared in iOS 7.0 . In order for the status bar in our application to be transparent, we need to do 3 things:


  1. Change info.plist, namely, change the UIViewControllerBasedStatusBarAppearance key :

UIViewControllerBasedStatusBarAppearance

  1. To display a QQuickView or QQuickWindow, use the showFullScreen () method instead of show ().

  1. Put in status bar style UIStatusBarStyleLightContent

If everything is clear with the first two points, we will analyze the third in more detail. You can change the style of the status bar by the following method on Objective-C:


[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];

Function returning the height of the status bar:


[UIApplication sharedApplication].statusBarFrame.size.height

In order for Objective-C code to work in the DeviceInfo class, we need to change the source resolution from .cpp to .mm. Therefore, in the .pro file, do the following:


HEADERS += \
    Include/DeviceInfo.h
!ios {
    SOURCES += \
        Source/DeviceInfo.cpp
}
ios {
    OBJECTIVE_SOURCES += \
        Source/DeviceInfo.mm
}

DeviceInfo.mm:


#include "DeviceInfo.h"
#import 
DeviceInfo::DeviceInfo(QObject *parent)
    : QObject(parent)
{
    [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
}
DeviceInfo &DeviceInfo::instance(QObject *parent)
{
    static DeviceInfo instance(parent);
    return instance;
}
int DeviceInfo::statusBarHeight()
{
    return [UIApplication sharedApplication].statusBarFrame.size.height;
}

The result on the screen:


ios screenshot


Conclusion


I tried to cover every step in as much detail as possible, so that, based on examples from the article, you could easily supplement your mobile project with native code. Look at the source code of the example on GitHub .


Also popular now: