Implement sharing on IOS

This commit is contained in:
Dorian Zedler 2021-06-25 23:22:28 +02:00
parent cc855bd329
commit 38ad92207e
20 changed files with 425 additions and 21 deletions

View file

@ -1,3 +1,9 @@
# Digital Rock ranking
App to view ranking and calendar data from https://www.digitalrock.de/
# Init
´´´
git submodule init
git submodule update
´´´

View file

@ -1,4 +1,4 @@
QT += quick qml quickcontrols2 purchasing printsupport
QT += quick qml quickcontrols2 purchasing
CONFIG += c++11
VERSION = 0.5.0
@ -15,7 +15,7 @@ DEFINES += QT_DEPRECATED_WARNINGS
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
INCLUDEPATH *= $$PWD/headers
INCLUDEPATH += $$PWD/headers
# Add version to define
DEFINES += APP_VERSION=\"\\\"$${VERSION}\\\"\"
@ -35,8 +35,6 @@ HEADERS += \
RESOURCES += resources/qml/qml.qrc \
resources/shared/shared.qrc \
#resources/shared/icons/bluerock/index.theme \
\ #$$files(resources/shared/icons/*.png, true)
resources/translations/translations.qrc
TRANSLATIONS += resources/translations/en.ts \
@ -54,7 +52,8 @@ else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
DISTFILES += \
CHANGELOG.md
CHANGELOG.md \
README.md
android {
QT += androidextras
@ -97,14 +96,54 @@ android {
}
ios {
OBJECTIVE_SOURCES += sources/shareUtils/ios/iosshareutils.mm \
sources/iospermissionutils.mm \
sources/shareUtils/ios/docviewcontroller.mm
HEADERS += headers/shareUtils/ios/iosshareutils.h \
headers/iospermissionutils.h \
headers/shareUtils/ios/docviewcontroller.h
OTHER_FILES += ios/Info.plist \
ios/blueROCK.entitlements
QMAKE_INFO_PLIST = ios/Info.plist
#QMAKE_IOS_DEPLOYMENT_TARGET = 12.0
#disable_warning.name = GCC_WARN_64_TO_32_BIT_CONVERSION
#disable_warning.value = NO
#QMAKE_MAC_XCODE_SETTINGS += disable_warning
# see https://bugreports.qt.io/browse/QTCREATORBUG-16968
# ios_signature.pri not part of project repo because of private signature details
# contains:
# QMAKE_XCODE_CODE_SIGN_IDENTITY = "iPhone Developer"
# MY_DEVELOPMENT_TEAM.name = DEVELOPMENT_TEAM
# MY_DEVELOPMENT_TEAM.value = your team Id from Apple Developer Account
# QMAKE_MAC_XCODE_SETTINGS += MY_DEVELOPMENT_TEAM
#include(ios_signature.pri)
QMAKE_ASSET_CATALOGS += resources/shared/Assets.xcassets
MY_ENTITLEMENTS.name = CODE_SIGN_ENTITLEMENTS
MY_ENTITLEMENTS.value = $$PWD/ios/blueROCK.entitlements
QMAKE_MAC_XCODE_SETTINGS += MY_ENTITLEMENTS
MY_BUNDLE_ID.name = PRODUCT_BUNDLE_IDENTIFIER
MY_BUNDLE_ID.value = de.itsblue.bluerock
QMAKE_MAC_XCODE_SETTINGS += MY_BUNDLE_ID
xcode_product_bundle_identifier_setting.value = "de.itsblue.bluerock"
PRODUCT_IDENTIFIER = de.itsblue.bluerock
}
CONFIG += enable_decoder_qr_code \
enable_encoder_qr_code \
qzxing_multimedia \
qzxing_qml
include(qzxing/src/QZXing-components.pri)
# this has to be the last line!

View file

@ -25,7 +25,6 @@
#include <QTimer>
#include <QUrl>
#include <QPainter>
#include <QPrinter>
#include <QPixmap>
#include <QBrush>
#include <QTextCharFormat>
@ -37,6 +36,8 @@
#ifdef Q_OS_ANDROID
#include <QtAndroidExtras>
#elif defined Q_OS_IOS
#include "iospermissionutils.h"
#endif
#include "shareUtils/shareutils.h"
@ -51,6 +52,7 @@ private:
QVariantMap _senddata(QUrl serviceUrl, QUrlQuery pdata = QUrlQuery());
ShareUtils* _shareUtils;
IosPermissionUtils* _iosPermissionUtils;
const QStringList _validBaseDomains = {"digitalrock.de", "bluerock.dev"};
bool _pendingIntentsChecked;
signals:

View file

@ -0,0 +1,22 @@
#ifndef IOSPERMISSIONUTILS_H
#define IOSPERMISSIONUTILS_H
#include <QObject>
#include <QEventLoop>
class IosPermissionUtils : QObject
{
Q_OBJECT
public:
IosPermissionUtils();
bool isCameraPermissionGranted();
bool requestCameraPermission();
private:
QEventLoop* _responseWaitLoop;
signals:
void permissionRequestFinished(bool result);
};
#endif // IOSPERMISSIONUTILS_H

View file

@ -18,7 +18,7 @@ public:
bool checkMimeTypeView(const QString &mimeType) override;
bool checkMimeTypeEdit(const QString &mimeType) override;
virtual QString getTemporaryFileLocationPath() override;
void shareText(const QString &text) override;
void shareText(const QString &text, const QUrl &url) override;
void sendFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId) override;
void viewFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId) override;
void editFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId) override;

View file

@ -0,0 +1,21 @@
// (c) 2017 Ekkehard Gentz (ekke) @ekkescorner
// my blog about Qt for mobile: http://j.mp/qt-x
// see also /COPYRIGHT and /LICENSE
#ifndef DOCVIEWCONTROLLER_HPP
#define DOCVIEWCONTROLLER_HPP
#import <UIKit/UIKit.h>
#import "iosshareutils.h"
@interface DocViewController : UIViewController <UIDocumentInteractionControllerDelegate>
@property int requestId;
@property IosShareUtils *mIosShareUtils;
@end
#endif // DOCVIEWCONTROLLER_HPP

View file

@ -0,0 +1,31 @@
// (c) 2017 Ekkehard Gentz (ekke) @ekkescorner
// my blog about Qt for mobile: http://j.mp/qt-x
// see also /COPYRIGHT and /LICENSE
#ifndef __IOSSHAREUTILS_H__
#define __IOSSHAREUTILS_H__
#include "headers/shareUtils/platformshareutils.h"
class IosShareUtils : public PlatformShareUtils
{
Q_OBJECT
public:
explicit IosShareUtils(QObject *parent = 0);
bool checkMimeTypeView(const QString &mimeType) override;
bool checkMimeTypeEdit(const QString &mimeType) override;
void shareText(const QString &text, const QUrl &url) override;
void sendFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId) override;
void viewFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId) override;
void editFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId) override;
void handleDocumentPreviewDone(const int &requestId);
public slots:
void handleFileUrlReceived(const QUrl &url);
void handleHttpsUrlReceived(const QUrl &url);
};
#endif

View file

@ -40,7 +40,7 @@ public:
virtual bool checkMimeTypeView(const QString &mimeType);
virtual bool checkMimeTypeEdit(const QString &mimeType);
virtual QString getTemporaryFileLocationPath();
virtual void shareText(const QString &text);
virtual void shareText(const QString &text, const QUrl &url);
virtual void sendFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId);
virtual void viewFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId);
virtual void editFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId);

View file

@ -49,7 +49,7 @@ public:
Q_INVOKABLE bool checkMimeTypeView(const QString &mimeType);
Q_INVOKABLE bool checkMimeTypeEdit(const QString &mimeType);
Q_INVOKABLE QString getTemporaryFileLocationPath();
Q_INVOKABLE void shareText(const QString &text);
Q_INVOKABLE void shareText(const QString &text, const QUrl &url);
Q_INVOKABLE void sendFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId);
Q_INVOKABLE void viewFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId);
Q_INVOKABLE void editFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId);

45
ios/Info.plist Normal file
View file

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDisplayName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string>${ASSETCATALOG_COMPILER_APPICON_NAME}</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>${QMAKE_SHORT_VERSION}</string>
<key>CFBundleSignature</key>
<string>${QMAKE_PKGINFO_TYPEINFO}</string>
<key>CFBundleVersion</key>
<string>${QMAKE_FULL_VERSION}</string>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>https</string>
</array>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>MinimumOSVersion</key>
<string>${IPHONEOS_DEPLOYMENT_TARGET}</string>
<key>NOTE</key>
<string>This file was generated by Qt/QMake.</string>
<key>NSCameraUsageDescription</key>
<string>blueROCK would like to access the camera.</string>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>

11
ios/blueROCK.entitlements Normal file
View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:l.bluerock.dev</string>
<string>applinks:app.bluerock.dev</string>
</array>
</dict>
</plist>

View file

@ -8,8 +8,8 @@ Rectangle {
property var flowchartData: ({})
// always unlock in debug mode
property bool unlocked: QT_DEBUG || appSettings.read("speedBackendPurchase") === "1"
//property bool unlocked: appSettings.read("speedBackendPurchase") === "1"
//property bool unlocked: QT_DEBUG || appSettings.read("speedBackendPurchase") === "1"
property bool unlocked: appSettings.read("speedBackendPurchase") === "1"
state: "hidden"

View file

@ -25,6 +25,8 @@ BlueRockBackend::BlueRockBackend(QObject *parent) : QObject(parent), _pendingInt
#if defined(Q_OS_ANDROID)
connect(qApp, SIGNAL(applicationStateChanged(Qt::ApplicationState)), this, SLOT(onApplicationStateChanged(Qt::ApplicationState)));
#elif defined Q_OS_IOS
this->_iosPermissionUtils = new IosPermissionUtils();
#endif
}
@ -96,7 +98,7 @@ QVariantMap BlueRockBackend::getParamsFromUrl(QString stringUrl) {
void BlueRockBackend::shareResultsAsUrl(QString url, QString compName) {
//% "Check out the results of %1 over here:"
this->_shareUtils->shareText(qtTrId("#shareResultsLinkText").arg(compName) + "\n" + url);
this->_shareUtils->shareText(qtTrId("#shareResultsLinkText").arg(compName) + " \n", url);
}
void BlueRockBackend::shareResultsAsPoster(QString url, QString compName) {
@ -146,6 +148,8 @@ bool BlueRockBackend::isCameraPermissionGranted() {
#ifdef Q_OS_ANDROID
QtAndroid::PermissionResult cameraAccess = QtAndroid::checkPermission("android.permission.CAMERA");
return cameraAccess == QtAndroid::PermissionResult::Granted;
#elif defined Q_OS_IOS
return this->_iosPermissionUtils->isCameraPermissionGranted();
#else
return true;
#endif
@ -203,6 +207,8 @@ bool BlueRockBackend::requestCameraPermission() {
}
return true;
#elif defined Q_OS_IOS
return this->_iosPermissionUtils->requestCameraPermission();
#else
return true;
#endif

View file

@ -0,0 +1,34 @@
#import "../headers/iospermissionutils.h"
#import <AVFoundation/AVFoundation.h>
#import <UIKit/UIKit.h>
IosPermissionUtils::IosPermissionUtils() : QObject(nullptr)
{
this->_responseWaitLoop = new QEventLoop(this);
}
bool IosPermissionUtils::isCameraPermissionGranted() {
AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
return status == AVAuthorizationStatusAuthorized;
}
bool IosPermissionUtils::requestCameraPermission() {
if(this->isCameraPermissionGranted())
return true;
if ([AVCaptureDevice respondsToSelector:@selector(requestAccessForMediaType: completionHandler:)]) {
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
// Will get here on both iOS 7 & 8 even though camera permissions weren't required
// until iOS 8. So for iOS 7 permission will always be granted.
this->_responseWaitLoop->exit(granted);
}];
}
if(!this->_responseWaitLoop->exec()) {
// permission was not granted -> open settings
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
return false;
}
return true;
}

View file

@ -20,7 +20,6 @@
#include <QGuiApplication>
#include <QtQml/QQmlContext>
#include <QQmlApplicationEngine>
#include <QStyleFactory>
#include <QUrl>
#include "QZXing.h"

View file

@ -58,9 +58,9 @@ QString AndroidShareUtils::getTemporaryFileLocationPath() {
return QStandardPaths::standardLocations(QStandardPaths::AppDataLocation).value(0) + "/temporaryFiles";
}
void AndroidShareUtils::shareText(const QString &text)
void AndroidShareUtils::shareText(const QString &text, const QUrl &url)
{
QAndroidJniObject jsText = QAndroidJniObject::fromString(text);
QAndroidJniObject jsText = QAndroidJniObject::fromString(text + url.toString());
jboolean ok = QAndroidJniObject::callStaticMethod<jboolean>("org/ekkescorner/utils/QShareUtils",
"shareText",
"(Ljava/lang/String;)Z",

View file

@ -0,0 +1,32 @@
// (c) 2017 Ekkehard Gentz (ekke) @ekkescorner
// my blog about Qt for mobile: http://j.mp/qt-x
// see also /COPYRIGHT and /LICENSE
#import "headers/shareUtils/ios/docviewcontroller.h"
#include <QDebug>
@interface DocViewController ()
@end
@implementation DocViewController
#pragma mark -
#pragma mark View Life Cycle
- (void)viewDidLoad {
[super viewDidLoad];
}
#pragma mark -
#pragma mark Document Interaction Controller Delegate Methods
- (UIViewController *) documentInteractionControllerViewControllerForPreview: (UIDocumentInteractionController *) controller {
#pragma unused (controller)
return self;
}
- (void)documentInteractionControllerDidEndPreview:(UIDocumentInteractionController *)controller
{
#pragma unused (controller)
qDebug() << "end preview";
self.mIosShareUtils->handleDocumentPreviewDone(self.requestId);
[self removeFromParentViewController];
}
@end

View file

@ -0,0 +1,156 @@
// (c) 2017 Ekkehard Gentz (ekke) @ekkescorner
// my blog about Qt for mobile: http://j.mp/qt-x
// see also /COPYRIGHT and /LICENSE
#import "headers/shareUtils/ios/iosshareutils.h"
#import <UIKit/UIKit.h>
#import <QGuiApplication>
#import <QQuickWindow>
#import <QDesktopServices>
#import <QUrl>
#import <QFileInfo>
#import <UIKit/UIDocumentInteractionController.h>
#import "headers/shareUtils/ios/docviewcontroller.h"
IosShareUtils::IosShareUtils(QObject *parent) : PlatformShareUtils(parent)
{
// Sharing Files from other iOS Apps I got the ideas and some code contribution from:
// Thomas K. Fischer (@taskfabric) - http://taskfabric.com - thx
QDesktopServices::setUrlHandler("file", this, "handleFileUrlReceived");
QDesktopServices::setUrlHandler("https", this, "handleHttpsUrlReceived");
}
bool IosShareUtils::checkMimeTypeView(const QString &mimeType) {
#pragma unused (mimeType)
// dummi implementation on iOS
// MimeType not used yet
return true;
}
bool IosShareUtils::checkMimeTypeEdit(const QString &mimeType) {
#pragma unused (mimeType)
// dummi implementation on iOS
// MimeType not used yet
return true;
}
void IosShareUtils::shareText(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];
}
// altImpl not used yet on iOS, on Android twi ways to use JNI
void IosShareUtils::sendFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId) {
#pragma unused (title, mimeType)
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];
if(![documentInteractionController presentPreviewAnimated:YES])
{
emit shareError(0, tr("No App found to open: %1").arg(filePath));
}
}
}
void IosShareUtils::viewFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId) {
#pragma unused (title, mimeType)
sendFile(filePath, title, mimeType, requestId);
}
void IosShareUtils::editFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId) {
#pragma unused (title, mimeType)
sendFile(filePath, title, mimeType, requestId);
}
void IosShareUtils::handleDocumentPreviewDone(const int &requestId)
{
// documentInteractionControllerDidEndPreview
qDebug() << "handleShareDone: " << requestId;
emit shareFinished(requestId);
}
void IosShareUtils::handleFileUrlReceived(const QUrl &url)
{
QString incomingUrl = url.toString();
if(incomingUrl.isEmpty()) {
qWarning() << "setFileUrlReceived: we got an empty URL";
emit shareError(0, tr("Empty URL received"));
return;
}
qDebug() << "IosShareUtils setFileUrlReceived: we got the File URL from iOS: " << incomingUrl;
QString myUrl;
if(incomingUrl.startsWith("file://")) {
myUrl= incomingUrl.right(incomingUrl.length()-7);
qDebug() << "QFile needs this URL: " << myUrl;
} else {
myUrl= incomingUrl;
}
// check if File exists
QFileInfo fileInfo = QFileInfo(myUrl);
if(fileInfo.exists()) {
emit fileUrlReceived(myUrl);
} else {
qDebug() << "setFileUrlReceived: FILE does NOT exist ";
emit shareError(0, tr("File does not exist: %1").arg(myUrl));
}
}
void IosShareUtils::handleHttpsUrlReceived(const QUrl &url)
{
if(url.isEmpty()) {
qWarning() << "handleHttpsUrlReceived: we got an empty URL";
emit shareError(0, tr("Empty URL received"));
return;
}
qDebug() << "IosShareUtils handleHttpsUrlReceived: we got the Other URL from IOS: " << url;
emit otherUrlReceived(url.toString(), "https");
}

View file

@ -20,8 +20,8 @@ bool PlatformShareUtils::checkMimeTypeEdit(const QString &mimeType) {
QString PlatformShareUtils::getTemporaryFileLocationPath() {
return QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
}
void PlatformShareUtils::shareText(const QString &text) {
qDebug() << text;
void PlatformShareUtils::shareText(const QString &text, const QUrl &url) {
qDebug() << text << url;
}
void PlatformShareUtils::sendFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId) {
qDebug() << filePath << " - " << title << "requestId " << requestId << " - " << mimeType << "altImpl? ";

View file

@ -5,7 +5,7 @@
#include "shareUtils/shareutils.h"
#ifdef Q_OS_IOS
#include "cpp/ios/iosshareutils.hpp"
#include "shareUtils/ios/iosshareutils.h"
#endif
#ifdef Q_OS_ANDROID
@ -62,9 +62,9 @@ QString ShareUtils::getTemporaryFileLocationPath()
return mPlatformShareUtils->getTemporaryFileLocationPath();
}
void ShareUtils::shareText(const QString &text)
void ShareUtils::shareText(const QString &text, const QUrl &url)
{
mPlatformShareUtils->shareText(text);
mPlatformShareUtils->shareText(text, url);
}
void ShareUtils::sendFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId)