app/sources/shareUtils/androidshareutils.cpp
2021-07-05 20:55:34 +02:00

379 lines
13 KiB
C++
Executable file

// (c) 2017 Ekkehard Gentz (ekke) @ekkescorner
// my blog about Qt for mobile: http://j.mp/qt-x
// see also /COPYRIGHT and /LICENSE
#include "shareUtils/androidshareutils.h"
#include <QUrl>
#include <QFileInfo>
#include <QDateTime>
#include <QtAndroidExtras/QAndroidJniObject>
#include <jni.h>
const static int RESULT_OK = -1;
const static int RESULT_CANCELED = 0;
AndroidShareUtils* AndroidShareUtils::mInstance = NULL;
AndroidShareUtils::AndroidShareUtils(QObject* parent) : PlatformShareUtils(parent)
{
// we need the instance for JNI Call
mInstance = this;
}
AndroidShareUtils* AndroidShareUtils::getInstance()
{
if (!mInstance) {
mInstance = new AndroidShareUtils;
qWarning() << "AndroidShareUtils should be instantiated !";
}
return mInstance;
}
bool AndroidShareUtils::checkMimeTypeView(const QString &mimeType)
{
QAndroidJniObject jsMime = QAndroidJniObject::fromString(mimeType);
jboolean verified = QAndroidJniObject::callStaticMethod<jboolean>("org/ekkescorner/utils/QShareUtils",
"checkMimeTypeView",
"(Ljava/lang/String;)Z",
jsMime.object<jstring>());
qDebug() << "View VERIFIED: " << mimeType << " - " << verified;
return verified;
}
bool AndroidShareUtils::checkMimeTypeEdit(const QString &mimeType)
{
QAndroidJniObject jsMime = QAndroidJniObject::fromString(mimeType);
jboolean verified = QAndroidJniObject::callStaticMethod<jboolean>("org/ekkescorner/utils/QShareUtils",
"checkMimeTypeEdit",
"(Ljava/lang/String;)Z",
jsMime.object<jstring>());
qDebug() << "Edit VERIFIED: " << mimeType << " - " << verified;
return verified;
}
QString AndroidShareUtils::getTemporaryFileLocationPath() {
return QStandardPaths::standardLocations(QStandardPaths::AppDataLocation).value(0) + "/temporaryFiles";
}
void AndroidShareUtils::shareText(const QString &text, const QUrl &url)
{
QAndroidJniObject jsText = QAndroidJniObject::fromString(text + url.toString());
jboolean ok = QAndroidJniObject::callStaticMethod<jboolean>("org/ekkescorner/utils/QShareUtils",
"shareText",
"(Ljava/lang/String;)Z",
jsText.object<jstring>());
if(!ok) {
qWarning() << "Unable to resolve activity from Java";
emit shareNoAppAvailable(0);
}
}
/*
* As default we're going the Java - way with one simple JNI call (recommended)
* if altImpl is true we're going the pure JNI way
*
* If a requestId was set we want to get the Activity Result back (recommended)
* We need the Request Id and Result Id to control our workflow
*/
void AndroidShareUtils::sendFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId)
{
mIsEditMode = false;
QAndroidJniObject jsPath = QAndroidJniObject::fromString(filePath);
QAndroidJniObject jsTitle = QAndroidJniObject::fromString(title);
QAndroidJniObject jsMimeType = QAndroidJniObject::fromString(mimeType);
jboolean ok = QAndroidJniObject::callStaticMethod<jboolean>("org/ekkescorner/utils/QShareUtils",
"sendFile",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)Z",
jsPath.object<jstring>(), jsTitle.object<jstring>(), jsMimeType.object<jstring>(), requestId);
if(!ok) {
qWarning() << "Unable to resolve activity from Java";
emit shareNoAppAvailable(requestId);
}
}
/*
* As default we're going the Java - way with one simple JNI call (recommended)
* if altImpl is true we're going the pure JNI way
*
* If a requestId was set we want to get the Activity Result back (recommended)
* We need the Request Id and Result Id to control our workflow
*/
void AndroidShareUtils::viewFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId)
{
mIsEditMode = false;
QAndroidJniObject jsPath = QAndroidJniObject::fromString(filePath);
QAndroidJniObject jsTitle = QAndroidJniObject::fromString(title);
QAndroidJniObject jsMimeType = QAndroidJniObject::fromString(mimeType);
jboolean ok = QAndroidJniObject::callStaticMethod<jboolean>("org/ekkescorner/utils/QShareUtils",
"viewFile",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)Z",
jsPath.object<jstring>(), jsTitle.object<jstring>(), jsMimeType.object<jstring>(), requestId);
if(!ok) {
qWarning() << "Unable to resolve activity from Java";
emit shareNoAppAvailable(requestId);
}
}
/*
* As default we're going the Java - way with one simple JNI call (recommended)
* if altImpl is true we're going the pure JNI way
*
* If a requestId was set we want to get the Activity Result back (recommended)
* We need the Request Id and Result Id to control our workflow
*/
void AndroidShareUtils::editFile(const QString &filePath, const QString &title, const QString &mimeType, const int &requestId)
{
mIsEditMode = true;
mCurrentFilePath = filePath;
QFileInfo fileInfo = QFileInfo(mCurrentFilePath);
mLastModified = fileInfo.lastModified().toSecsSinceEpoch();
qDebug() << "LAST MODIFIED: " << mLastModified;
QAndroidJniObject jsPath = QAndroidJniObject::fromString(filePath);
QAndroidJniObject jsTitle = QAndroidJniObject::fromString(title);
QAndroidJniObject jsMimeType = QAndroidJniObject::fromString(mimeType);
jboolean ok = QAndroidJniObject::callStaticMethod<jboolean>("org/ekkescorner/utils/QShareUtils",
"editFile",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)Z",
jsPath.object<jstring>(), jsTitle.object<jstring>(), jsMimeType.object<jstring>(), requestId);
if(!ok) {
qWarning() << "Unable to resolve activity from Java";
emit shareNoAppAvailable(requestId);
}
}
// used from QAndroidActivityResultReceiver
void AndroidShareUtils::handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data)
{
Q_UNUSED(data);
qDebug() << "From JNI QAndroidActivityResultReceiver: " << receiverRequestCode << "ResultCode:" << resultCode;
processActivityResult(receiverRequestCode, resultCode);
}
// used from Activity.java onActivityResult()
void AndroidShareUtils::onActivityResult(int requestCode, int resultCode)
{
qDebug() << "From Java Activity onActivityResult: " << requestCode << "ResultCode:" << resultCode;
processActivityResult(requestCode, resultCode);
}
void AndroidShareUtils::processActivityResult(int requestCode, int resultCode)
{
// we're getting RESULT_OK only if edit is done
if(resultCode == RESULT_OK) {
emit shareEditDone(requestCode);
} else if(resultCode == RESULT_CANCELED) {
if(mIsEditMode) {
// Attention: not all Apps will give you the correct ResultCode:
// Google Fotos will send OK if saved and CANCELED if canceled
// Some Apps always sends CANCELED even if you modified and Saved the File
// so you should check the modified Timestamp of the File to know if
// you should emit shareEditDone() or shareFinished() !!!
QFileInfo fileInfo = QFileInfo(mCurrentFilePath);
qint64 currentModified = fileInfo.lastModified().toSecsSinceEpoch();
qDebug() << "CURRENT MODIFIED: " << currentModified;
if(currentModified > mLastModified) {
emit shareEditDone(requestCode);
return;
}
}
emit shareFinished(requestCode);
} else {
qDebug() << "wrong result code: " << resultCode << " from request: " << requestCode;
emit shareError(requestCode, "Share: an Error occured");
}
}
void AndroidShareUtils::checkPendingIntents(const QString workingDirPath)
{
QAndroidJniObject activity = QtAndroid::androidActivity();
if(activity.isValid()) {
// create a Java String for the Working Dir Path
QAndroidJniObject jniWorkingDir = QAndroidJniObject::fromString(workingDirPath);
if(!jniWorkingDir.isValid()) {
qWarning() << "QAndroidJniObject jniWorkingDir not valid.";
emit shareError(0, "Share: an Error occured\nWorkingDir not valid");
return;
}
activity.callMethod<void>("checkPendingIntents","(Ljava/lang/String;)V", jniWorkingDir.object<jstring>());
qDebug() << "checkPendingIntents: " << workingDirPath;
return;
}
qDebug() << "checkPendingIntents: Activity not valid";
}
void AndroidShareUtils::setFileUrlReceived(const QString &url)
{
if(url.isEmpty()) {
qWarning() << "setFileUrlReceived: we got an empty URL";
emit shareError(0, "Empty URL received");
return;
}
qDebug() << "AndroidShareUtils setFileUrlReceived: we got the File URL from JAVA: " << url;
QString myUrl;
if(url.startsWith("file://")) {
myUrl= url.right(url.length()-7);
qDebug() << "QFile needs this URL: " << myUrl;
} else {
myUrl= url;
}
// check if File exists
QFileInfo fileInfo = QFileInfo(myUrl);
if(fileInfo.exists()) {
emit fileUrlReceived(myUrl);
} else {
qDebug() << "setFileUrlReceived: FILE does NOT exist ";
emit shareError(0, QString("File does not exist: %1").arg(myUrl));
}
}
void AndroidShareUtils::setOtherUrlReceived(const QString &url, const QString &scheme)
{
if(url.isEmpty()) {
qWarning() << "setFileUrlReceived: we got an empty URL";
emit shareError(0, "Empty URL received");
return;
}
qDebug() << "AndroidShareUtils setOtherUrlReceived: we got the Other URL from JAVA: " << url;
emit otherUrlReceived(url, scheme);
}
void AndroidShareUtils::setFileReceivedAndSaved(const QString &url)
{
if(url.isEmpty()) {
qWarning() << "setFileReceivedAndSaved: we got an empty URL";
emit shareError(0, "Empty URL received");
return;
}
qDebug() << "AndroidShareUtils setFileReceivedAndSaved: we got the File URL from JAVA: " << url;
QString myUrl;
if(url.startsWith("file://")) {
myUrl= url.right(url.length()-7);
qDebug() << "QFile needs this URL: " << myUrl;
} else {
myUrl= url;
}
// check if File exists
QFileInfo fileInfo = QFileInfo(myUrl);
if(fileInfo.exists()) {
emit fileReceivedAndSaved(myUrl);
} else {
qDebug() << "setFileReceivedAndSaved: FILE does NOT exist ";
emit shareError(0, QString("File does not exist: %1").arg(myUrl));
}
}
// to be safe we check if a File Url from java really exists for Qt
// if not on the Java side we'll try to read the content as Stream
bool AndroidShareUtils::checkFileExits(const QString &url)
{
if(url.isEmpty()) {
qWarning() << "checkFileExits: we got an empty URL";
emit shareError(0, "Empty URL received");
return false;
}
qDebug() << "AndroidShareUtils checkFileExits: we got the File URL from JAVA: " << url;
QString myUrl;
if(url.startsWith("file://")) {
myUrl= url.right(url.length()-7);
qDebug() << "QFile needs this URL: " << myUrl;
} else {
myUrl= url;
}
// check if File exists
QFileInfo fileInfo = QFileInfo(myUrl);
if(fileInfo.exists()) {
qDebug() << "Yep: the File exists for Qt";
return true;
} else {
qDebug() << "Uuups: FILE does NOT exist ";
return false;
}
}
// instead of defining all JNICALL as demonstrated below
// there's another way, making it easier to manage all the methods
// see https://www.kdab.com/qt-android-episode-5/
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT void JNICALL
Java_de_itsblue_blueROCK_MainActivity_setFileUrlReceived(JNIEnv *env,
jobject obj,
jstring url)
{
const char *urlStr = env->GetStringUTFChars(url, NULL);
Q_UNUSED (obj)
AndroidShareUtils::getInstance()->setFileUrlReceived(urlStr);
env->ReleaseStringUTFChars(url, urlStr);
return;
}
JNIEXPORT void JNICALL
Java_de_itsblue_blueROCK_MainActivity_setOtherUrlReceived(JNIEnv *env,
jobject obj,
jstring url,
jstring scheme)
{
const char *urlStr = env->GetStringUTFChars(url, NULL);
const char *schemeStr = env->GetStringUTFChars(scheme, NULL);
Q_UNUSED (obj)
AndroidShareUtils::getInstance()->setOtherUrlReceived(urlStr, schemeStr);
env->ReleaseStringUTFChars(url, urlStr);
env->ReleaseStringUTFChars(scheme, schemeStr);
return;
}
JNIEXPORT void JNICALL
Java_de_itsblue_blueROCK_MainActivity_setFileReceivedAndSaved(JNIEnv *env,
jobject obj,
jstring url)
{
const char *urlStr = env->GetStringUTFChars(url, NULL);
Q_UNUSED (obj)
AndroidShareUtils::getInstance()->setFileReceivedAndSaved(urlStr);
env->ReleaseStringUTFChars(url, urlStr);
return;
}
JNIEXPORT bool JNICALL
Java_de_itsblue_blueROCK_MainActivity_checkFileExits(JNIEnv *env,
jobject obj,
jstring url)
{
const char *urlStr = env->GetStringUTFChars(url, NULL);
Q_UNUSED (obj)
bool exists = AndroidShareUtils::getInstance()->checkFileExits(urlStr);
env->ReleaseStringUTFChars(url, urlStr);
return exists;
}
JNIEXPORT void JNICALL
Java_de_itsblue_blueROCK_MainActivity_fireActivityResult(JNIEnv *env,
jobject obj,
jint requestCode,
jint resultCode)
{
Q_UNUSED (obj)
Q_UNUSED (env)
AndroidShareUtils::getInstance()->onActivityResult(requestCode, resultCode);
return;
}
#ifdef __cplusplus
}
#endif