Details
-
Bug
-
Resolution: Unresolved
-
P2: Important
-
None
-
6.9.0
-
None
Description
I reported a similar bug on Qt 6.2.2 (https://bugreports.qt.io/browse/QTBUG-47979), was apparently fixed with Qt 6.4.1.
Today, I'm upgrading to Qt 6.9.0, and it fails again. With a different error than the one reported for Qtbug 47979, that's why I prefer filling a new issue rather than reopening the old one.
The code remains the same:
main.cpp:
#include <QApplication> #include "mainwindow.h" int main( int argc, char* argv[] ) { QApplication app(argc, argv); MainFrame frame; frame.show(); return app.exec(); }
mainwindow.h:
#pragma once #include <QMainWindow> class MainFrame : public QMainWindow { Q_OBJECT public: MainFrame(); public slots: void openFromQt(); void openFromSDK(); private: void fixOpenFile(); std::string fileName; };
mainwindow.cpp:
#include "mainwindow.h" #include <QVBoxLayout> #include <QPushButton> #include <QFile> #include <QMessageBox> #include "qandroidextras_p.h" #define QtAndroid QtAndroidPrivate #define requestPermissionsSync requestPermissions #include <QJniObject> #include <QJniEnvironment> #include <QDesktopServices> #include <QUrl> #include <QDebug> #include <QStandardPaths> #include <string> #include <fstream> MainFrame::MainFrame() { // bug reported for Qt 6.2.2 here: https://bugreports.qt.io/browse/QTBUG-47979 // workarounded by https://bugreports.qt.io/browse/QTBUG-67877 // new bug reported for Qt 6.9.0 fileName = (QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + "/" + CURRENT_MODULE_NAME + ".txt").toStdString(); auto result1 = QtAndroid::requestPermission("android.permission.WRITE_EXTERNAL_STORAGE"); result1.waitForFinished(); if ( result1.result() != QtAndroid::PermissionResult::Authorized ) { qCritical() << "Failed to get privileges for writting"; } auto result2 = QtAndroid::requestPermission("android.permission.READ_EXTERNAL_STORAGE"); result2.waitForFinished(); if ( result2.result() != QtAndroid::PermissionResult::Authorized ) { qCritical() << "Failed to get privileges for writting"; } // create a file: std::fstream file; file.open( fileName.c_str(), std::ios_base::out ); file << "Hello World!" << std::endl; file.close(); if (QFile(fileName.c_str()).exists()) QMessageBox::information(NULL,"","File exists"); else QMessageBox::information(NULL,"","File does not exist"); QWidget* centralWidget = new QWidget( this ); centralWidget->setLayout( new QVBoxLayout() ); QPushButton* qt_button = new QPushButton( "Open with Qt", centralWidget ); QObject::connect( qt_button, SIGNAL(clicked()), this, SLOT(openFromQt()) ); centralWidget->layout()->addWidget( qt_button ); QPushButton* sdk_button = new QPushButton( "Open with SDK", centralWidget ); QObject::connect( sdk_button, SIGNAL(clicked()), this, SLOT(openFromSDK()) ); centralWidget->layout()->addWidget( sdk_button ); setCentralWidget( centralWidget ); } void MainFrame::fixOpenFile() { // see https://bugreports.qt.io/browse/QTBUG-67877 QAndroidJniObject DisableDeathOnFileUriExposureObject("my/common/DisableDeathOnFileUriExposureHelper","()V"); if ( DisableDeathOnFileUriExposureObject.isValid() ) { jboolean worked = DisableDeathOnFileUriExposureObject.callMethod<jboolean>("doDisableDeathOnFileUriExposure","()Z"); if ( worked ) qDebug() << "OK"; else qDebug() << "KO"; } else { assert( false ); } } void MainFrame::openFromQt() { fixOpenFile(); // use Qt code to open a file. Works on PC, not on Android QUrl url = QUrl::fromLocalFile(fileName.c_str()); QDesktopServices::openUrl(url); } std::string GetFileType( const std::string& fileName ) { QAndroidJniObject javaFileName = QAndroidJniObject::fromString(fileName.c_str()); //type is valid QAndroidJniObject extension = QAndroidJniObject::callStaticObjectMethod("android/webkit/MimeTypeMap", "getFileExtensionFromUrl", "(Ljava/lang/String;)Ljava/lang/String;", javaFileName.object<jobject>()); if ( extension.isValid() ) { std::string ext = extension.toString().toStdString(); QAndroidJniObject mime = QAndroidJniObject::callStaticObjectMethod("android/webkit/MimeTypeMap", "getSingleton", "()Landroid/webkit/MimeTypeMap;"); if ( mime.isValid() ) { QAndroidJniObject type = mime.callObjectMethod("getMimeTypeFromExtension", "(Ljava/lang/String;)Ljava/lang/String;", extension.object<jobject>() ); if ( type.isValid() ) { return type.toString().toSDEString(); } } } return "text/plain"; } bool startIntentToOpenFile( QAndroidJniObject& activity, const std::string& fileName, bool separatApp ) { bool opened = false; if ( activity.isValid() ) { QAndroidJniObject intent("android/content/Intent","()V"); if ( intent.isValid() ) { QAndroidJniObject name = QAndroidJniObject::fromString(fileName.c_str()); //type is valid std::string mimeType = GetFileType(fileName); QAndroidJniObject type = QAndroidJniObject::fromString(mimeType.c_str()); //type is valid // should we actually use QAndroidJniObject::getStaticObjectField<jstring>("android/intent/action","ACTION_VIEW"); ? QAndroidJniObject action = QAndroidJniObject::fromString("android.intent.action.VIEW"); if ( type.isValid() && name.isValid() && action.isValid() ) { QAndroidJniObject file( "java/io/File","(Ljava/lang/String;)V",name.object<jobject>()); if ( file.isValid() ) { jboolean exists = file.callMethod<jboolean>("exists","()Z"); if ( exists ) { QAndroidJniObject uri = QAndroidJniObject::callStaticObjectMethod("android/net/Uri", "fromFile", "(Ljava/io/File;)Landroid/net/Uri;", file.object<jobject>()); if ( uri.isValid() ) { //QAndroidJniObject string = uri.callObjectMethod("toString","()Ljava/lang/String;"); //const char *str1 = string.toString().toStdString().c_str(); intent.callObjectMethod("setDataAndType","(Landroid/net/Uri;Ljava/lang/String;)Landroid/content/Intent;",uri.object<jobject>(),type.object<jobject>()); intent.callObjectMethod("setAction","(Ljava/lang/String;)Landroid/content/Intent;",action.object<jobject>()); if ( separatApp ) intent.callObjectMethod("setFlags","(I)Landroid/content/Intent;",0x10000000); if ( intent.isValid() ) { //activity.callObjectMethod("startActivity","(Landroid/content/Intent;)V",intent.object<jobject>()); // recommended by https://bugreports.qt-project.org/browse/QTBUG-41395 activity.callMethod<void>("startActivity","(Landroid/content/Intent;)V",intent.object<jobject>()); opened = true; } } } } } } } return opened; } void MainFrame::openFromSDK() { fixOpenFile(); auto activity = QJniObject(QNativeInterface::QAndroidApplication::context()); startIntentToOpenFile( activity, fileName, false ); }
DisableDeathOnFileUriExposureHelper.java:
package my.common; import java.lang.reflect.*; import android.os.StrictMode; public class DisableDeathOnFileUriExposureHelper { public boolean doDisableDeathOnFileUriExposure() { try{ Method m = StrictMode.class.getMethod("disableDeathOnFileUriExposure"); m.invoke(null); return true; }catch(Exception e){ e.printStackTrace(); return false; } } }
`fixOpenFile` and are here to workaround https://bugreports.qt.io/browse/QTBUG-67877
In the end:
- Opening the file with the SDK (MainFrame::openFromSDK) works. This means trhe file exists for good and we have permission to access it
- Opening the file with QDesktopServices (MainFrame::openFromQt()) fails, even if `DisableDeathOnFileUriExposureHelper` was called.
The error reported is a new one:
W/default (26178): java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/Documents/qtbug_androidopenfile.txt
W/default (26178): at androidx.core.content.FileProvider$SimplePathStrategy.getUriForFile(FileProvider.java:867)
W/default (26178): at androidx.core.content.FileProvider.getUriForFile(FileProvider.java:467)
W/default (26178): at org.qtproject.qt.android.QtNative.startQtApplication(Native Method)
W/default (26178): at org.qtproject.qt.android.QtNative$$ExternalSyntheticLambda4.run(D8$$SyntheticClass:0)
W/default (26178): at org.qtproject.qt.android.QtThread$1.run(QtThread.java:25)
W/default (26178): at java.lang.Thread.run(Thread.java:764)
W/default (26178):
Attachments
Issue Links
- replaces
-
QTBUG-47979 QDesktopServices is unable to open a file on Android
-
- Closed
-