Skip to content Skip to sidebar Skip to footer

How To Make A Call With Qt Directly From The Application?

I want to implement a dialer-feature in my app. Actually, it's done, but it works the way I don't want to. When button is pressed, native dialer opens and waiting for pressing a bu

Solution 1:

Whereas in iOS the call can be issued directly, the same does not apply to Android. To overcome the problem you can define a C++ class Wrapper which handles the call, depending on the current OS. An instance of this class is registered as a context property and directly used in QML.

Inside the class you can exploit Android native APIs which provide the automatic dialing feature via the Intent action ACTION_CALL (but remember that there are some restrictions in using it). Typically in Android you write:

IntentcallIntent=newcallIntent(Intent.ACTION_CALL);
callIntent.setPackage("com.android.phone");          // force native dialer  (Android < 5)
callIntent.setPackage("com.android.server.telecom"); // force native dialer  (Android >= 5)
callIntent.setData(Uri.parse("tel:" + number));
callIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(callIntent);

By setting the package we can force the usage of the native dialer. Without it the user would be prompt to choose among available dialers (i.e. Skype, Viber, etc...) clearly if other are installed on that device. The system dialer package changed between Lollipop and the previous releases so that it is necessary to check the SDK at runtime to set the correct one.


To call these APIs in C++ you need the Qt Android Extras and in particular QAndroidJniObject but also the related permissions in your custom Android manifest. Just add to your .pro file:

android:QT+=androidextras#included only in Android builds

and the following row to your manifest:

<uses-permissionandroid:name="android.permission.CALL_PHONE"/>

If you did not define a custom manifest just add one. As of Qt Creator 3.3 just go to Projects > Build > Build Android APK > Create Templates to generate the custom manifest.


The header of our class looks like the following - constructor/deconstructor missing:

#ifndef WRAPPER_H#define WRAPPER_H#include<QObject>#include<QString>#include<QDebug>#if defined(Q_OS_IOS)#include<QUrl>#include<QDesktopServices>#elif defined(Q_OS_ANDROID)#include<QtAndroid>#include<QAndroidJniObject>#endif#include<QDesktopServices>#include<QUrl>classWrapper: public QObject
{
    Q_OBJECT
public:
    Q_INVOKABLE voiddirectCall(QString number);
};

#endif// WRAPPER_H

The corresponding source file looks like the following - again constructor/deconstructor missing:

#include "wrapper.h"void Wrapper::directCall(QString number)
{
#if defined(Q_OS_IOS)
    QDesktopServices::openUrl(QUrl(QString("tel://%1").arg(number)));
#elif defined(Q_OS_ANDROID)// get the Qt android activity
    QAndroidJniObject activity =  QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;");
    //if (activity.isValid()){
    // real Java code to C++ code// Intent callIntent = new callIntent(Intent.ACTION_CALL);
    QAndroidJniObject callConstant = QAndroidJniObject::getStaticObjectField<jstring>("android/content/Intent", "ACTION_CALL");
    QAndroidJniObject callIntent("android/content/Intent",  "(Ljava/lang/String;)V", callConstant.object());
    // callIntent.setPackage("com.android.phone"); (<= 4.4w)  intent.setPackage("com.android.server.telecom");  (>= 5)
    QAndroidJniObject package;
    if(QtAndroid::androidSdkVersion() >= 21)
        package = QAndroidJniObject::fromString("com.android.server.telecom");
    else
        package = QAndroidJniObject::fromString("com.android.phone");
    callIntent.callObjectMethod("setPackage", "(Ljava/lang/String;)Landroid/content/Intent;", package.object<jstring>());
    // callIntent.setData(Uri.parse("tel:" + number));
    QAndroidJniObject jNumber = QAndroidJniObject::fromString(QString("tel:%1").arg(number));
    QAndroidJniObject uri = QAndroidJniObject::callStaticObjectMethod("android/net/Uri","parse","(Ljava/lang/String;)Landroid/net/Uri;", jNumber.object());
    callIntent.callObjectMethod("setData", "(Landroid/net/Uri;)Landroid/content/Intent;", uri.object<jobject>());
    // callIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    jint flag = QAndroidJniObject::getStaticField<jint>("android/content/Intent", "FLAG_ACTIVITY_NEW_TASK");
    callIntent.callObjectMethod("setFlags", "(I)Landroid/content/Intent;", flag);
    //startActivity(callIntent);
    activity.callMethod<void>("startActivity","(Landroid/content/Intent;)V", callIntent.object<jobject>());
}
    else
        qDebug() << "Something wrong with Qt activity...";
#else
    qDebug() << "Does nothing here...";
#endif
}

As discussed at the beginning, you can include an instance of this class as a context property. The main for this purpose looks like the following:

#include<QApplication>#include<QQmlContext>#include<QQmlApplicationEngine>#include"wrapper.h"intmain(int argc, char *argv[]){
    QApplication app(argc, argv);

    QQmlApplicationEngine engine;
    Wrapper jw;
    engine.rootContext()->setContextProperty("caller", &jw);
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));      

    return app.exec();
}

Finally in QML you can simply write:

Button {
    id: callButton
    anchors.centerIn: parent
    text: 'Make a call'
    onClicked: caller.directCall("+0123456789")
}

The code can be easily extended to support also WinPhone while maintaining the same QML interface (probably via the inclusion of a dedicated header/source pair). Finally, the usage of conditional inclusions guarantees that the code correctly compiles even if the used kit is changed on the fly.

As a final note, I would add that Google Play policies are not as strict as Apple App Store policies. Hence, an app rejection due to the usage of ACTION_CALL is not likely to happen.

Solution 2:

you will need the permission

<uses-permissionandroid:name="android.permission.CALL_PHONE" />

in your AndroidManifest.xml

in java you would then do:

IntentdialIntent=newIntent(Intent.ACTION_CALL, Uri.parse("tel:+1123123"));
startActivity(dialIntent);

the equivalent Qt code is something like:

QAndroidJniObjectaction= QAndroidJniObject::fromString("android.intent.action.CALL");
QAndroidJniObjecturiString= QAndroidJniObject::fromString("tel:+1123123");
QAndroidJniObjecturi= QAndroidJniObject::callStaticObjectMethod("android/net/Uri", "parse", "(Ljava/lang/String)V", uriString);


QAndroidJniObject intent("android/content/Intent","(Ljava/lang/String, Landroid/net/Uri)V", action, uri);


QAndroidJniObjectactivity= QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;");
activity.callObjectMethod("startActivity","(Landroid/content/Intent;)V",intent.object<jobject>());

however, note that using ACTION_CALL may get you reject from the appstore, and google advises to use ACTION_DIAL, which opens the dialer instead of doing a direct call.

Post a Comment for "How To Make A Call With Qt Directly From The Application?"