Share from a QML app on iOS and Android

What if you want to add a share feature to your cross platform QML app? Share on Facebook, Twitter, Email etc...

QML side

We will use a custom component that will let us share a text and a URL.

import QtQuick 2.3  
import QtQuick.Controls 1.2  
// Our import
import com.lasconic 1.0

ApplicationWindow {  
    visible: true
    width: 640
    height: 480
    title: qsTr("Share Utils")

    //Custom component
    ShareUtils {
        id: shareUtils
    }

    Button {
        id: share
        text: "Share lasconic's blog"
        anchors.centerIn: parent
        onClicked: {
            //simple call
            shareUtils.share("Interesting blog", "http://blog.lasconic.com")
        }
    }
}

The component needs to be registered in the main.cpp of the app.

qmlRegisterType<ShareUtils> ("com.lasconic", 1, 0, "ShareUtils");

Now, of course, we need to implement the component on iOS and Android.

C++ side

The main C++ class is a just a delegate for the OS specific implementation.

ShareUtils::ShareUtils(QQuickItem *parent)  
    : QQuickItem(parent)
{
#if defined(Q_OS_IOS)
    _pShareUtils = new IosShareUtils(this);
#elif defined(Q_OS_ANDROID)
    _pShareUtils = new AndroidShareUtils(this);
#else
    _pShareUtils = new PlatformShareUtils(this);
#endif
}

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

On Android

C++

We will just use JNI on the C++ side to call the Java side and the Android API. The Qt AndroidExtras package is handy.

void AndroidShareUtils::share(const QString &text, const QUrl &url)  
{
    QAndroidJniObject jsText = QAndroidJniObject::fromString(text);
    QAndroidJniObject jsUrl = QAndroidJniObject::fromString(url.toString());
    QAndroidJniObject::callStaticMethod<void>("com/musescore/QShareUtils",
                                       "share",
                                       "(Ljava/lang/String;Ljava/lang/String;)V",
                                       jsText.object<jstring>(), jsUrl.object<jstring>());
}

In the pro file, we need to add QT += androidextras to be able to use QAndroidJniObject.

Java

We use QtNative to get the current activity and then it's pure Android API code. It will display all the apps that can deal with the text/plain mime type. We could choose which one we want to be listed, add a subject field etc... but it's beyond the scope of this article. A good intro here.

import org.qtproject.qt5.android.QtNative;

public static void share(String text, String url) {  
        if (QtNative.activity() == null)
            return;
        Intent sendIntent = new Intent();
        sendIntent.setAction(Intent.ACTION_SEND);
        sendIntent.putExtra(Intent.EXTRA_TEXT, text + " " + url);
        sendIntent.setType("text/plain");
        QtNative.activity().startActivity(sendIntent);
    }

On iOS

#import <UIKit/UIKit.h>
#import <QtGui/qpa/qplatformnativeinterface.h>
#import <QGuiApplication>
#import <QQuickWindow>

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 UIViewController
UIViewController *qtController = [[UIApplication sharedApplication].keyWindow rootViewController];

    UIActivityViewController *activityController = [[UIActivityViewController alloc] initWithActivityItems:sharingItems applicationActivities:nil];
    [qtController presentViewController:activityController animated:YES completion:nil];
}

Conclusion

Et voilà, the QML app will be able to pass a text and a URL to the underlying operating system and the user can enjoy a native UI to share to the social media of his choice.

Full code: https://github.com/lasconic/ShareUtils-QML