Compare commits

...

1 Commits

Author SHA1 Message Date
Roberto Viola
d157685be4 crossqfile issue
https://github.com/mahdize/CrossQFile/issues/3
2023-03-26 17:56:25 +02:00
7 changed files with 230 additions and 7 deletions

165
src/CrossQFile.cpp Normal file
View File

@@ -0,0 +1,165 @@
#include "CrossQFile.h"
#include <QFileInfo>
#ifdef __ANDROID__
#include <jni.h>
#include <QtAndroidExtras/QtAndroid>
#include <QtAndroidExtras/qandroidjnienvironment.h>
#endif
#include <QCoreApplication>
CrossQFile::CrossQFile(const QString& nameOrUri, const bool isUri) : QFile(nameOrUri), isWorkingWithUri(isUri){
#ifdef __ANDROID__
mainActivityObj = QtAndroid::androidActivity();
contentResolverObj = mainActivityObj.callObjectMethod
("getContentResolver","()Landroid/content/ContentResolver;");
checkJenvExceptions();
#endif
}
#ifdef __ANDROID__
bool CrossQFile::checkJenvExceptions() const{
QAndroidJniEnvironment env;
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
return true;
}
return false;
}
QAndroidJniObject CrossQFile::parseUriString(const QString& uriString) const{
return QAndroidJniObject::callStaticObjectMethod
("android/net/Uri" , "parse",
"(Ljava/lang/String;)Landroid/net/Uri;",
QAndroidJniObject::fromString(uriString).object());
}
#endif
void CrossQFile::setFileName(const QString& nameOrUri, bool isUri){
QFile::setFileName(nameOrUri);
isWorkingWithUri = isUri;
}
qint64 CrossQFile::size() const{
#ifdef __ANDROID__
if(isWorkingWithUri){
QAndroidJniObject cursorObj {contentResolverObj.callObjectMethod
("query",
"(Landroid/net/Uri;[Ljava/lang/String;Landroid/os/Bundle;Landroid/os/CancellationSignal;)Landroid/database/Cursor;",
parseUriString(fileName()).object(), QAndroidJniObject().object(), QAndroidJniObject().object()
, QAndroidJniObject().object(), QAndroidJniObject().object())};
int sizeIndex {cursorObj.callMethod<jint>
("getColumnIndex","(Ljava/lang/String;)I",
QAndroidJniObject::getStaticObjectField<jstring>
("android/provider/OpenableColumns","SIZE").object())};
cursorObj.callMethod<jboolean>("moveToFirst");
qint64 ret {cursorObj.callMethod<jlong>("getLong","(I)J",sizeIndex)};
if(checkJenvExceptions()){
ret = 0;
}
return ret;
}
#endif
return QFile::size();
}
bool CrossQFile::open(CrossQFile::OpenMode openMode){
#ifdef __ANDROID__
if(isWorkingWithUri){
QAndroidJniObject jopenMode {QAndroidJniObject::fromString("rw")};
switch (openMode){
case QFile::ReadOnly:
jopenMode = QAndroidJniObject::fromString("r");
break;
case QFile::WriteOnly:
jopenMode = QAndroidJniObject::fromString("w");
break;
default:
jopenMode = QAndroidJniObject::fromString("rw");
}
QAndroidJniObject pfdObj{contentResolverObj.callObjectMethod
("openFileDescriptor", "(Landroid/net/Uri;Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;",
parseUriString(fileName()).object(), jopenMode.object())};
int fd{pfdObj.callMethod<jint>("detachFd")};
bool ret {false};
if(QFile::open(fd, openMode)){
ret = true;
}
if(checkJenvExceptions()){
ret = false;
}
return ret;
}
#endif
return QFile::open(openMode);
}
QString CrossQFile::displayName(){
#ifdef __ANDROID__
if(isWorkingWithUri){
QAndroidJniObject cursorObj {contentResolverObj.callObjectMethod
("query",
"(Landroid/net/Uri;[Ljava/lang/String;Landroid/os/Bundle;Landroid/os/CancellationSignal;)Landroid/database/Cursor;",
parseUriString(fileName()).object(), QAndroidJniObject().object(), QAndroidJniObject().object(),
QAndroidJniObject().object(), QAndroidJniObject().object())};
cursorObj.callMethod<jboolean>("moveToFirst");
QAndroidJniObject retObj{cursorObj.callObjectMethod
("getString","(I)Ljava/lang/String;", cursorObj.callMethod<jint>
("getColumnIndex","(Ljava/lang/String;)I",
QAndroidJniObject::getStaticObjectField<jstring>
("android/provider/OpenableColumns","DISPLAY_NAME").object()))};
QString ret {retObj.toString()};
if(checkJenvExceptions()){
ret = "";
}
return ret;
}
#endif
QFileInfo fileInfo(fileName());
return fileInfo.fileName();
}
bool CrossQFile::remove(){
#ifdef __ANDROID__
if(isWorkingWithUri){
bool ret {static_cast<bool>(QAndroidJniObject::callStaticMethod<jboolean>
("android/provider/DocumentsContract", "deleteDocument",
"(Landroid/content/ContentResolver;Landroid/net/Uri;)Z",
contentResolverObj.object(), parseUriString(fileName()).object()))};
if(checkJenvExceptions()){
ret = false;
}
return ret;
}
#endif
return QFile::remove();
}
bool CrossQFile::rename(const QString& newName){
if(!isWorkingWithUri){
return QFile::rename(newName);
}else{
return false;
}
}
bool CrossQFile::exists() const{
#ifdef __ANDROID__
if(isWorkingWithUri){
bool ret {static_cast<bool>(QAndroidJniObject::callStaticMethod<jboolean>
("android/provider/DocumentsContract", "isDocumentUri",
"(Landroid/content/Context;Landroid/net/Uri;)Z",
mainActivityObj.object(), parseUriString(fileName()).object()))};
if(checkJenvExceptions()){
ret = false;
}
return ret;
}
#endif
return QFile::exists();
}

40
src/CrossQFile.h Normal file
View File

@@ -0,0 +1,40 @@
#ifndef CROSSQFILE_H
#define CROSSQFILE_H
#include <QObject>
#include <QFile>
#include <QSharedPointer>
#ifdef __ANDROID__
#include <QtAndroidExtras/QAndroidJniObject>
#endif
class CrossQFile : public QFile
{
public:
CrossQFile(const QString& nameOrUri, const bool isUri = false);
virtual qint64 size() const override;
//if working with uri, it uses QAndroidjniObject to open the file using the uri.
//otherwise it would act like a normal QFile.
bool open(CrossQFile::OpenMode openMode) override;
bool remove();
//if working with uri, it does nothing.
//otherwise it would act like a normal QFile.
bool rename(const QString& newName);
bool exists() const;
//returns the display name of the file.
QString displayName();
void setFileName(const QString& nameOrUri, bool isUri = false);
private:
bool isWorkingWithUri{false};
#ifdef __ANDROID__
QAndroidJniObject mainActivityObj;
QAndroidJniObject contentResolverObj;
bool checkJenvExceptions() const;
QAndroidJniObject parseUriString(const QString& uriString) const;
#endif
};
#endif // CROSSQFILE_H

View File

@@ -106,6 +106,7 @@
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="com.android.vending.BILLING"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="com.google.android.things.permission.USE_PERIPHERAL_IO" />
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"

View File

@@ -466,6 +466,14 @@ int main(int argc, char *argv[]) {
qDebug() << "READ_EXTERNAL_STORAGE denied!";
}
result = QtAndroid::checkPermission(QString("android.permission.MANAGE_EXTERNAL_STORAGE"));
if (result == QtAndroid::PermissionResult::Denied) {
QtAndroid::PermissionResultMap resultHash =
QtAndroid::requestPermissionsSync(QStringList({"android.permission.MANAGE_EXTERNAL_STORAGE"}));
if (resultHash["android.permission.MANAGE_EXTERNAL_STORAGE"] == QtAndroid::PermissionResult::Denied)
qDebug() << "MANAGE_EXTERNAL_STORAGE denied!";
}
result = QtAndroid::checkPermission(QString("android.permission.ACCESS_FINE_LOCATION"));
if (result == QtAndroid::PermissionResult::Denied) {
QtAndroid::PermissionResultMap resultHash =

View File

@@ -63,6 +63,7 @@ DEFINES += QT_DEPRECATED_WARNINGS IO_UNDER_QT SMTP_BUILD
# include(../qtzeroconf/qtzeroconf.pri)
SOURCES += \
$$PWD/CrossQFile.cpp \
$$PWD/androidactivityresultreceiver.cpp \
$$PWD/androidadblog.cpp \
$$PWD/apexbike.cpp \
@@ -259,6 +260,7 @@ else: unix:!android: target.path = /opt/$${TARGET}/bin
INCLUDEPATH += fit-sdk/
HEADERS += \
$$PWD/CrossQFile.h \
$$PWD/androidactivityresultreceiver.h \
$$PWD/androidadblog.h \
$$PWD/apexbike.h \

View File

@@ -984,13 +984,18 @@ bool trainprogram::saveXML(const QString &filename, const QList<trainrow> &rows)
void trainprogram::save(const QString &filename) { saveXML(filename, rows); }
trainprogram *trainprogram::load(const QString &filename, bluetooth *b) {
if (!filename.right(3).toUpper().compare(QStringLiteral("ZWO"))) {
// on android we can't check anymore the extension
// because it returns something like this
// content://com.android.providers.downloads.documents/document/126
// reference https://stackoverflow.com/questions/58715547/how-to-open-a-file-in-android-with-qt-having-the-content-uri
QString description = "";
QString tags = "";
return new trainprogram(zwiftworkout::load(filename, &description, &tags), b, &description, &tags);
QString description = "";
QString tags = "";
QList<trainrow> r = zwiftworkout::load(filename, &description, &tags);
qDebug() << r.length();
if(r.length() > 0) {
return new trainprogram(r, b, &description, &tags);
} else {
return new trainprogram(loadXML(filename), b);
}
}

View File

@@ -1,4 +1,5 @@
#include "zwiftworkout.h"
#include "CrossQFile.h"
#include <QDebug>
#include <QJsonDocument>
#include <QJsonObject>
@@ -8,8 +9,9 @@
QList<trainrow> zwiftworkout::load(const QString &filename, QString *description, QString *tags) {
QSettings settings;
// QList<trainrow> list; //NOTE: clazy-unuzed-non-trivial-variable
QFile input(filename);
CrossQFile input(filename, true);
input.open(QIODevice::ReadOnly);
qDebug() << input.size();
return load(input.readAll(), description, tags);
}
@@ -325,7 +327,7 @@ QList<trainrow> zwiftworkout::load(const QByteArray &input, QString *description
if (tags != nullptr)
tags->clear();
while (!stream.atEnd()) {
while (!stream.atEnd()) {
stream.readNext();
QString name = stream.name().toString();
QString text = stream.text().toString();