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. Android 13 Devices – API 30+ - SelectFiles – CreateFiles – Qt 5.15

Android 13 Devices – API 30+ - SelectFiles – CreateFiles – Qt 5.15

Scheduled Pinned Locked Moved Unsolved Mobile and Embedded
scopedstorageapi 30shared storagefiledialogandroid 11
7 Posts 2 Posters 1.8k 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.
  • E Offline
    E Offline
    ekkescorner
    Qt Champions 2016
    wrote on 23 Nov 2022, 09:14 last edited by ekkescorner
    #1

    In one of my apps the user must be able to select one or more files from shared data and also to create a new file in a selected directory.
    Up to API 29 I did this using QStandardPathes, per ex. for PicturesLocation and DocumentsLocation and have built my own CustomFileDialog.

    With API30 QStandardPathes only work with AppDataLocation, but not with external shared data because of ScopedStorage.

    Qt support for ScopedStorage is still work-in-progress (https://bugreports.qt.io/browse/QTBUG-98974)

    Tried to use MANAGE_EXTERNAL_STORAGE, because the app is a DropBox-like app to manage files from shared network drives or SharePoints.

    cpp:

    #define PACKAGE_NAME "package:org.company.package_name"
    jboolean value = QAndroidJniObject::callStaticMethod<jboolean>("android/os/Environment", "isExternalStorageManager");
    if( value == false )
    { 
    QAndroidJniObject ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION = QAndroidJniObject::getStaticObjectField( "android/provider/Settings", "ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION","Ljava/lang/String;" ); 
    QAndroidJniObject intent("android/content/Intent", "(Ljava/lang/String;)V", ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION.object()); 
    QAndroidJniObject jniPath = QAndroidJniObject::fromString(PACKAGE_NAME); 
    QAdroidJniObject jniUri = QAndroidJniObject::callStaticObjectMethod("android/net/Uri", "parse", "(Ljava/lang/String;)Landroid/net/Uri;", jniPath.object<jstring>());
    QAndroidJniObject jniResult = intent.callObjectMethod("setData", "(Landroid/net/Uri;)Landroid/content/Intent;", jniUri.object<jobject>() );
    QtAndroid::startActivity(intent, 0); 
    }
    

    Manifest:

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
    

    App asks for permission and sets permission – controlled at settings-apps-special apps-apps with all files access.

    But nothing changed – QStandardPathes still gives no access to files – only for internal AppDataLocation.

    Any idea what could be missed to get the “old” behaviour back using MANAGE_EXTERNAL_STORAGE permission ?

    Looked for workarounds and tried KDAB SharedStorage Library:
    https://www.kdab.com/android-shared-storage-qt-wrapper/
    https://github.com/KDAB/android/tree/master/shared_storage
    YEP - it works, but there’s a drawback:
    selecting files only selects one file – haven’t found a way to select multiple files using KDABs SharedStorage library and this is essential for users.
    Selecting a folder and then creating a file inside the folder works great.

    Then I found out, that QDir and QFile not only support file pathes, but also Content URIs (https://developer.android.com/reference/android/content/ContentUris).

    This is not documented yet, because full ScopedStorage support not ready yet (https://bugreports.qt.io/browse/QTBUG-99664)

    But the current implementation helps if using the QML FileDialog on Android :)

    Select multi files: Using the QML FileDialog I can see the files from different external locations and I can select multi files, what was missing from KDABs SharedStorage. I’m getting a list of ContentUris and can then use QFile directly using the ContentUris :)

    Create File in folder: I can select a folder, was asked for permission, but then trying to create a file inside the selected folder, I’m getting errors:

    E Qt JAVA : openFdForContentUrl(): No permissions to open Uri
    

    and “Unknown error” from QFile, per ex. using
    "content://com.android.externalstorage.documents/tree/primary%3ADocuments/ekke.txt"

    Unfortunately using the QML FileDialog I’m getting some warnings:

    W libenbwDOCS_x_arm64-v8a.so: qrc:/android_rcc_bundle/qml/QtQuick/Controls/Styles/Android/LabelStyle.qml:94: TypeError: Cannot read property 'ENABLED_SELECTED_STATE_SET' of undefined
    W libenbwDOCS_x_arm64-v8a.so: qrc:/android_rcc_bundle/qml/QtQuick/Controls/Styles/Android/LabelStyle.qml:89: TypeError: Cannot read property 'ENABLED_STATE_SET' of undefined
    

    Conclusion:
    Selecting a folder and creating a file I can do using KDABs SharedStorage,
    Selecting multiple files I can do using QML FileDialog and have to live with the warnings.

    Any better ideas?
    Thx
    ekke

    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
    • J Offline
      J Offline
      JoeCFD
      wrote on 23 Nov 2022, 19:24 last edited by JoeCFD
      #2

      call the following code at start-up. You add read as well. Try it out.

          bool ok{ true };
          auto write_permission = QtAndroid::checkPermission( "android.permission.WRITE_EXTERNAL_STORAGE" );
          if ( write_permission == QtAndroid::PermissionResult::Denied) {
              QtAndroid::requestPermissionsSync( QStringList() << "android.permission.WRITE_EXTERNAL_STORAGE" );
              write_permission = QtAndroid::checkPermission("android.permission.WRITE_EXTERNAL_STORAGE");
              if ( write_permission == QtAndroid::PermissionResult::Denied ) {
                   ok = false;
              }
          }
      

      Note that any permission has to be approved by users, not your code. This seems to be the policies of Android.

      E 1 Reply Last reply 23 Nov 2022, 21:14
      0
      • J JoeCFD
        23 Nov 2022, 19:24

        call the following code at start-up. You add read as well. Try it out.

            bool ok{ true };
            auto write_permission = QtAndroid::checkPermission( "android.permission.WRITE_EXTERNAL_STORAGE" );
            if ( write_permission == QtAndroid::PermissionResult::Denied) {
                QtAndroid::requestPermissionsSync( QStringList() << "android.permission.WRITE_EXTERNAL_STORAGE" );
                write_permission = QtAndroid::checkPermission("android.permission.WRITE_EXTERNAL_STORAGE");
                if ( write_permission == QtAndroid::PermissionResult::Denied ) {
                     ok = false;
                }
            }
        

        Note that any permission has to be approved by users, not your code. This seems to be the policies of Android.

        E Offline
        E Offline
        ekkescorner
        Qt Champions 2016
        wrote on 23 Nov 2022, 21:14 last edited by ekkescorner
        #3

        @JoeCFD thx. checked again my customer app, where I also got MANAGE_EXTERNAL_STORAGE and I'm already doing this:

        #if defined(Q_OS_ANDROID)
        bool DataUtil::checkPermission() {
            QtAndroid::PermissionResult r = QtAndroid::checkPermission("android.permission.WRITE_EXTERNAL_STORAGE");
            if(r == QtAndroid::PermissionResult::Denied) {
                QtAndroid::requestPermissionsSync( QStringList() << "android.permission.WRITE_EXTERNAL_STORAGE" );
                r = QtAndroid::checkPermission("android.permission.WRITE_EXTERNAL_STORAGE");
                if(r == QtAndroid::PermissionResult::Denied) {
                    qDebug() << "Permission denied";
                    return false;
                }
            }
            qDebug() << "YEP: Permission WRITE_EXTERNAL_STORAGE OK";
            return true;
        }
        #endif
        

        this worked until API 29, but not for API 30+

        also verified my test app for QFileDialog - and there I'm also checking permission

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

        E 1 Reply Last reply 24 Nov 2022, 17:25
        0
        • E ekkescorner
          23 Nov 2022, 21:14

          @JoeCFD thx. checked again my customer app, where I also got MANAGE_EXTERNAL_STORAGE and I'm already doing this:

          #if defined(Q_OS_ANDROID)
          bool DataUtil::checkPermission() {
              QtAndroid::PermissionResult r = QtAndroid::checkPermission("android.permission.WRITE_EXTERNAL_STORAGE");
              if(r == QtAndroid::PermissionResult::Denied) {
                  QtAndroid::requestPermissionsSync( QStringList() << "android.permission.WRITE_EXTERNAL_STORAGE" );
                  r = QtAndroid::checkPermission("android.permission.WRITE_EXTERNAL_STORAGE");
                  if(r == QtAndroid::PermissionResult::Denied) {
                      qDebug() << "Permission denied";
                      return false;
                  }
              }
              qDebug() << "YEP: Permission WRITE_EXTERNAL_STORAGE OK";
              return true;
          }
          #endif
          

          this worked until API 29, but not for API 30+

          also verified my test app for QFileDialog - and there I'm also checking permission

          E Offline
          E Offline
          ekkescorner
          Qt Champions 2016
          wrote on 24 Nov 2022, 17:25 last edited by
          #4

          @ekkescorner as written I was able to get Content URIs from FileDialog using a small test app:

          content://com.android.externalstorage.documents/document/primary%3ADocuments%2Fekke.txt
          

          now I wanted to implement this in my customer app. unfortunately now the Content URIs from FileDialog look different:

          content://com.android.providers.media.documents/document/document%3A1000000020
          

          this URI also gives me access to the file content, but I'm missing the name and suffix
          any idea what could cause this different behavior ?

          same environment, same QML FileDialog from QtQuick.Dialogs1.3 inside a QQC2 app
          only difference: customer app is using user 10 (Business app)

          thx
          ekke

          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
          0
          • J Offline
            J Offline
            JoeCFD
            wrote on 24 Nov 2022, 17:45 last edited by
            #5

            can you try QStandardPaths for write and read? I remember I used them. When I get a chance, I will test for you. I built my app for Android 11(API 30) and 12(API 31).

            E 1 Reply Last reply 24 Nov 2022, 18:05
            0
            • J JoeCFD
              24 Nov 2022, 17:45

              can you try QStandardPaths for write and read? I remember I used them. When I get a chance, I will test for you. I built my app for Android 11(API 30) and 12(API 31).

              E Offline
              E Offline
              ekkescorner
              Qt Champions 2016
              wrote on 24 Nov 2022, 18:05 last edited by
              #6

              @JoeCFD this is what I did before: used a custom FileDialog based on QStandardPathes. But with API30 only QStandardPath for 'internal' data with AppDataLocation is working - all others like PicturesLocation, DocumentsLocation are empty.
              Up to API 29 all works well.
              I'm using Qt 5.15.7 - will update to 5.15.11 next week

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

              E 1 Reply Last reply 12 Jan 2023, 12:24
              0
              • E ekkescorner
                24 Nov 2022, 18:05

                @JoeCFD this is what I did before: used a custom FileDialog based on QStandardPathes. But with API30 only QStandardPath for 'internal' data with AppDataLocation is working - all others like PicturesLocation, DocumentsLocation are empty.
                Up to API 29 all works well.
                I'm using Qt 5.15.7 - will update to 5.15.11 next week

                E Offline
                E Offline
                ekkescorner
                Qt Champions 2016
                wrote on 12 Jan 2023, 12:24 last edited by
                #7

                @ekkescorner Assam Boudjelthia has done a great work on this and now prepared cherry-picks for 5.15 🙂
                see QTBUG-98974
                can someone help and test ?

                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
                0

                • Login

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