Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. Mobile and Embedded
  4. How to convert Android content url and use it to open file?
QtWS25 Last Chance

How to convert Android content url and use it to open file?

Scheduled Pinned Locked Moved Unsolved Mobile and Embedded
androidfile dialogiosmobilefile
10 Posts 8 Posters 6.5k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • H Offline
    H Offline
    Hitokage
    wrote on last edited by Hitokage
    #1

    Hey, I am using FileDialog in QML to pick a file on Android but the path I get is in this content:// format. Is there any way to convert this and use it for example as QFile url? Right now I'm getting (even after using toLocalFile() from QUrl ):

    Filename: "content://com.android.providers.downloads.documents/document/381"
    QFSFileEngine::open: No file name specified
    

    Working on a multiplatform mobile app so I'd like to be able to use this at least on iOS too.
    Thanks!

    EDIT: Since FileDialog seems to be having more problems in Android right now I have created my own file picker using FolderListModel and StandardPaths. It's not an answer to this question but it is probably safer solution that works on all platforms.

    J.HilkJ 1 Reply Last reply
    0
    • sierdzioS Offline
      sierdzioS Offline
      sierdzio
      Moderators
      wrote on last edited by
      #2

      You need a file provider which will translate the file name into something that Java (or C++) backend can understand. Unfortunately it's platform-specific and quite complicated. My answer to this question might help, although it's incomplete (sorry!).

      (Z(:^

      1 Reply Last reply
      0
      • H Hitokage

        Hey, I am using FileDialog in QML to pick a file on Android but the path I get is in this content:// format. Is there any way to convert this and use it for example as QFile url? Right now I'm getting (even after using toLocalFile() from QUrl ):

        Filename: "content://com.android.providers.downloads.documents/document/381"
        QFSFileEngine::open: No file name specified
        

        Working on a multiplatform mobile app so I'd like to be able to use this at least on iOS too.
        Thanks!

        EDIT: Since FileDialog seems to be having more problems in Android right now I have created my own file picker using FolderListModel and StandardPaths. It's not an answer to this question but it is probably safer solution that works on all platforms.

        J.HilkJ Offline
        J.HilkJ Offline
        J.Hilk
        Moderators
        wrote on last edited by
        #3

        @Hitokage I would suggest taking a look at this blog post:

        https://www.qt.io/blog/2017/12/01/sharing-files-android-ios-qt-app

        It is a rather in-depth post with working code that sends and receives files. It's not perfect but very good, you should be able to adapt it easily enough 😉


        Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


        Q: What's that?
        A: It's blue light.
        Q: What does it do?
        A: It turns blue.

        1 Reply Last reply
        3
        • H Offline
          H Offline
          Hitokage
          wrote on last edited by
          #4

          @J-Hilk @sierdzio thank you guys!

          1 Reply Last reply
          0
          • R Offline
            R Offline
            R-Type
            wrote on last edited by
            #5

            with Qt 5.15.2 I can open and read content:// path directly passing it to QFile. According to this it is possible since Qt 5.13.

            1 Reply Last reply
            1
            • M Offline
              M Offline
              mahdi.ze
              wrote on last edited by
              #6

              you can use CrossQFile. CrossQFile is a QFile that supports android uri.
              https://github.com/mahdize/CrossQFile

              1 Reply Last reply
              1
              • S Offline
                S Offline
                Soviet-Ball
                wrote on last edited by
                #7

                @Hitokage
                Hi,
                you can try this function below.
                (I use class QJniObject in Qt6, just replace it with QAndroidJniObject :D)

                static QString getRealPathFromUri(const QUrl &url)
                {
                    QString path = "";
                
                    QFileInfo info = QFileInfo(url.toString());
                    if(info.isFile())
                    {
                        QString abs = QFileInfo(url.toString()).absoluteFilePath();
                        if(!abs.isEmpty() && abs != url.toString() && QFileInfo(abs).isFile())
                        {
                            return abs;
                        }
                    }
                    else if(info.isDir())
                    {
                        QString abs = QFileInfo(url.toString()).absolutePath();
                        if(!abs.isEmpty() && abs != url.toString() && QFileInfo(abs).isDir())
                        {
                            return abs;
                        }
                    }
                    QString localfile = url.toLocalFile();
                    if((QFileInfo(localfile).isFile() || QFileInfo(localfile).isDir()) && localfile != url.toString())
                    {
                        return localfile;
                    }
                #ifdef Q_OS_ANDROID
                    QJniObject jUrl = QJniObject::fromString(url.toString());
                    QJniObject jContext = QtAndroidPrivate::context();
                    QJniObject jContentResolver = jContext.callObjectMethod("getContentResolver", "()Landroid/content/ContentResolver;");
                    QJniObject jUri = QJniObject::callStaticObjectMethod("android/net/Uri", "parse", "(Ljava/lang/String;)Landroid/net/Uri;", jUrl.object<jstring>());
                    QJniObject jCursor = jContentResolver.callObjectMethod("query", "(Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;", jUri.object<jobject>(), nullptr, nullptr, nullptr, nullptr);
                    QJniObject jScheme = jUri.callObjectMethod("getScheme", "()Ljava/lang/String;");
                    QJniObject authority;
                    if(jScheme.isValid())
                    {
                        authority = jUri.callObjectMethod("getAuthority", "()Ljava/lang/String;");
                    }
                    if(authority.isValid() && authority.toString() == "com.android.externalstorage.documents")
                    {
                        QJniObject jPath = jUri.callObjectMethod("getPath", "()Ljava/lang/String;");
                        path = jPath.toString();
                    }
                    else if(jCursor.isValid() && jCursor.callMethod<jboolean>("moveToFirst"))
                    {
                        QJniObject jColumnIndex = QJniObject::fromString("_data");
                        jint columnIndex = jCursor.callMethod<jint>("getColumnIndexOrThrow", "(Ljava/lang/String;)I", jColumnIndex.object<jstring>());
                        QJniObject jRealPath = jCursor.callObjectMethod("getString", "(I)Ljava/lang/String;", columnIndex);
                        path = jRealPath.toString();
                        if(authority.isValid() && authority.toString().startsWith("com.android.providers") && !url.toString().startsWith("content://media/external/"))
                        {
                            QStringList list = path.split(":");
                            if(list.count() == 2)
                            {
                                QString type = list.at(0);
                                QString id = list.at(1);
                                if(type == "image")
                                    type = type + "s";
                                if(type == "document" || type == "documents")
                                    type = "file";
                                if(type == "msf")
                                    type = "downloads";
                                if(QList<QString>({"images","video","audio"}).contains(type))
                                    type = type + "/media";
                                path = "content://media/external/"+type;
                                path = path + "/" + id;
                                return getRealPathFromUri(path);
                            }
                        }
                    }
                    else
                    {
                        QJniObject jPath = jUri.callObjectMethod("getPath", "()Ljava/lang/String;");
                        path = jPath.toString();
                        qDebug() << QFile::exists(path) <<path;
                    }
                
                    if(path.startsWith("primary:"))
                    {
                        path = path.remove(0,QString("primary:").length());
                        path = "/sdcard/" + path;
                    }
                    else if(path.startsWith("/document/primary:"))
                    {
                        path = path.remove(0,QString("/document/primary:").length());
                        path = "/sdcard/" + path;
                    }
                    else if(path.startsWith("/tree/primary:"))
                    {
                        path = path.remove(0,QString("/tree/primary:").length());
                        path = "/sdcard/" + path;
                    }
                    else if(path.startsWith("/storage/emulated/0/"))
                    {
                        path = path.remove(0,QString("/storage/emulated/0/").length());
                        path = "/sdcard/" + path;
                    }
                    else if(path.startsWith("/tree//"))
                    {
                        path = path.remove(0,QString("/tree//").length());
                        path = "/" + path;
                    }
                    if(!QFileInfo(path).isFile() && !QFileInfo(path).isDir() && !path.startsWith("/data"))
                        return url.toString();
                    return path;
                #else
                    return url.toString();
                #endif
                
                }
                
                ekkescornerE 1 Reply Last reply
                1
                • S Soviet-Ball

                  @Hitokage
                  Hi,
                  you can try this function below.
                  (I use class QJniObject in Qt6, just replace it with QAndroidJniObject :D)

                  static QString getRealPathFromUri(const QUrl &url)
                  {
                      QString path = "";
                  
                      QFileInfo info = QFileInfo(url.toString());
                      if(info.isFile())
                      {
                          QString abs = QFileInfo(url.toString()).absoluteFilePath();
                          if(!abs.isEmpty() && abs != url.toString() && QFileInfo(abs).isFile())
                          {
                              return abs;
                          }
                      }
                      else if(info.isDir())
                      {
                          QString abs = QFileInfo(url.toString()).absolutePath();
                          if(!abs.isEmpty() && abs != url.toString() && QFileInfo(abs).isDir())
                          {
                              return abs;
                          }
                      }
                      QString localfile = url.toLocalFile();
                      if((QFileInfo(localfile).isFile() || QFileInfo(localfile).isDir()) && localfile != url.toString())
                      {
                          return localfile;
                      }
                  #ifdef Q_OS_ANDROID
                      QJniObject jUrl = QJniObject::fromString(url.toString());
                      QJniObject jContext = QtAndroidPrivate::context();
                      QJniObject jContentResolver = jContext.callObjectMethod("getContentResolver", "()Landroid/content/ContentResolver;");
                      QJniObject jUri = QJniObject::callStaticObjectMethod("android/net/Uri", "parse", "(Ljava/lang/String;)Landroid/net/Uri;", jUrl.object<jstring>());
                      QJniObject jCursor = jContentResolver.callObjectMethod("query", "(Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;", jUri.object<jobject>(), nullptr, nullptr, nullptr, nullptr);
                      QJniObject jScheme = jUri.callObjectMethod("getScheme", "()Ljava/lang/String;");
                      QJniObject authority;
                      if(jScheme.isValid())
                      {
                          authority = jUri.callObjectMethod("getAuthority", "()Ljava/lang/String;");
                      }
                      if(authority.isValid() && authority.toString() == "com.android.externalstorage.documents")
                      {
                          QJniObject jPath = jUri.callObjectMethod("getPath", "()Ljava/lang/String;");
                          path = jPath.toString();
                      }
                      else if(jCursor.isValid() && jCursor.callMethod<jboolean>("moveToFirst"))
                      {
                          QJniObject jColumnIndex = QJniObject::fromString("_data");
                          jint columnIndex = jCursor.callMethod<jint>("getColumnIndexOrThrow", "(Ljava/lang/String;)I", jColumnIndex.object<jstring>());
                          QJniObject jRealPath = jCursor.callObjectMethod("getString", "(I)Ljava/lang/String;", columnIndex);
                          path = jRealPath.toString();
                          if(authority.isValid() && authority.toString().startsWith("com.android.providers") && !url.toString().startsWith("content://media/external/"))
                          {
                              QStringList list = path.split(":");
                              if(list.count() == 2)
                              {
                                  QString type = list.at(0);
                                  QString id = list.at(1);
                                  if(type == "image")
                                      type = type + "s";
                                  if(type == "document" || type == "documents")
                                      type = "file";
                                  if(type == "msf")
                                      type = "downloads";
                                  if(QList<QString>({"images","video","audio"}).contains(type))
                                      type = type + "/media";
                                  path = "content://media/external/"+type;
                                  path = path + "/" + id;
                                  return getRealPathFromUri(path);
                              }
                          }
                      }
                      else
                      {
                          QJniObject jPath = jUri.callObjectMethod("getPath", "()Ljava/lang/String;");
                          path = jPath.toString();
                          qDebug() << QFile::exists(path) <<path;
                      }
                  
                      if(path.startsWith("primary:"))
                      {
                          path = path.remove(0,QString("primary:").length());
                          path = "/sdcard/" + path;
                      }
                      else if(path.startsWith("/document/primary:"))
                      {
                          path = path.remove(0,QString("/document/primary:").length());
                          path = "/sdcard/" + path;
                      }
                      else if(path.startsWith("/tree/primary:"))
                      {
                          path = path.remove(0,QString("/tree/primary:").length());
                          path = "/sdcard/" + path;
                      }
                      else if(path.startsWith("/storage/emulated/0/"))
                      {
                          path = path.remove(0,QString("/storage/emulated/0/").length());
                          path = "/sdcard/" + path;
                      }
                      else if(path.startsWith("/tree//"))
                      {
                          path = path.remove(0,QString("/tree//").length());
                          path = "/" + path;
                      }
                      if(!QFileInfo(path).isFile() && !QFileInfo(path).isDir() && !path.startsWith("/data"))
                          return url.toString();
                      return path;
                  #else
                      return url.toString();
                  #endif
                  
                  }
                  
                  ekkescornerE Offline
                  ekkescornerE Offline
                  ekkescorner
                  Qt Champions 2016
                  wrote on last edited by
                  #8

                  @Soviet-Ball you don't need this anmore in Qt 6.6:
                  FileDialog on Android supports content URLs.

                  QFileInfo().fileName()
                  

                  gives you the file path
                  so easy now :)

                  ekke ... Qt Champion 2016 | 2024 ... mobile business apps
                  5.15 --> 6.8 https://t1p.de/ekkeChecklist
                  QMake --> CMake https://t1p.de/ekkeCMakeMobileApps

                  S 1 Reply Last reply
                  1
                  • ekkescornerE ekkescorner

                    @Soviet-Ball you don't need this anmore in Qt 6.6:
                    FileDialog on Android supports content URLs.

                    QFileInfo().fileName()
                    

                    gives you the file path
                    so easy now :)

                    S Offline
                    S Offline
                    shokarta
                    wrote on last edited by
                    #9

                    @ekkescorner how does it gives me path? as of 6.7.2 it gives only the proper filename...
                    so i still dont know where the file is stored in order to open it :/

                    ekkescornerE 1 Reply Last reply
                    0
                    • S shokarta

                      @ekkescorner how does it gives me path? as of 6.7.2 it gives only the proper filename...
                      so i still dont know where the file is stored in order to open it :/

                      ekkescornerE Offline
                      ekkescornerE Offline
                      ekkescorner
                      Qt Champions 2016
                      wrote on last edited by ekkescorner
                      #10

                      @shokarta try with Android FileDialog

                      FileDialog {
                          title: qsTr("Select a File")
                          fileMode: FileDialog.OpenFile
                          onAccepted: {
                          if(selectedFiles.length) {
                               console.log("we selected: ", selectedFiles[0])
                      

                      Selected File 'München.pdf' from FileDialog
                      ...this gives you per ex:

                      content://com.android.providers.downloads.documents/document/32
                      

                      // you can check from C++ and verify fileUrl:

                      QFileInfo fileInfo(fileUrl);
                      qDebug() << "verifying fileUrl: " << fileUrl;
                      qDebug() << "BASE: " << fileInfo.baseName();
                      qDebug() << "FileName: " << fileInfo.fileName();
                      qDebug() << "Path: " << fileInfo.path();
                      qDebug() << "absoluteFilePath: " << fileInfo.absoluteFilePath();
                      return fileInfo.exists();
                      

                      this gives you:

                      verifying fileUrl:  "content://com.android.providers.downloads.documents/document/32"
                      BASE:  "München"
                      FileName:  "München.pdf"
                      Path:  "content://com.android.providers.downloads.documents/document" 
                      absoluteFilePath:  "content://com.android.providers.downloads.documents/document/32"
                      file exists
                      

                      ekke ... Qt Champion 2016 | 2024 ... mobile business apps
                      5.15 --> 6.8 https://t1p.de/ekkeChecklist
                      QMake --> CMake https://t1p.de/ekkeCMakeMobileApps

                      1 Reply Last reply
                      1

                      • Login

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • Users
                      • Groups
                      • Search
                      • Get Qt Extensions
                      • Unsolved