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 get file URI using JNI objects
Forum Updated to NodeBB v4.3 + New Features

Android get file URI using JNI objects

Scheduled Pinned Locked Moved Unsolved Mobile and Embedded
6 Posts 2 Posters 3.0k Views 1 Watching
  • 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.
  • S Offline
    S Offline
    Syl1211
    wrote on last edited by
    #1

    Hi all,

    I discovered the Qt android development tools a few months ago and I'm therefore discovering new functionalities everyday.
    For now, I need to import the URI of a file (any .txt file) in order to extract and parse its content.
    What I'm trying to do is to open all possibilities of online/offline file storage databases (i.e. dropbox, google drive, or internal file manager) using Intentions.

    Currently, I can choose one of those file databases by clicking on a custom qml button that launches the "openImportOptions" method described below, browse into it and pick a file. After the file being clicked the application goes back to my main screen. It works using this code:

    ImportSettingsRetrieveResultReceiver.h

    #ifndef IMPORTSETTINGSRETRIEVERESULTRECEIVER_H
    #define IMPORTSETTINGSRETRIEVERESULTRECEIVER_H
    
    #include <QAndroidActivityResultReceiver>
    #include <QAndroidJniEnvironment>
    #include <QAndroidJniObject>
    #include <QtAndroid>
    #include <QObject>
    
    class ImportSettingsRetrieveResultReceiver : public QObject, public QAndroidActivityResultReceiver
    {
    
        Q_OBJECT
    
    public:
    
        ImportSettingsRetrieveResultReceiver();
    
        void openImportOptions();
        void handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data) override;
    
    };
    
    #endif // IMPORTSETTINGSRETRIEVERESULTRECEIVER_H
    

    importsettingsretrieveresultreceiver.cpp

    #include "importsettingsretrieveresultreceiver.h"
    #include <QDebug>
    
    
    ImportSettingsRetrieveResultReceiver::ImportSettingsRetrieveResultReceiver(){
    
    }
    
    
    void ImportSettingsRetrieveResultReceiver::openImportOptions(){
    
        QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;");   //activity is valid
        if ( activity.isValid() )
        {
    
            // Equivalent to Jave code: 'Intent intent = new Intent();'
            QAndroidJniObject intent("android/content/Intent","()V");
    
            if ( intent.isValid() ){
    
                QAndroidJniObject Intent__ACTION_GET_CONTENT = QAndroidJniObject::getStaticObjectField("android/content/Intent", "ACTION_GET_CONTENT", "Ljava/lang/String;");
                QAndroidJniObject Intent__CATEGORY_OPENABLE = QAndroidJniObject::getStaticObjectField("android/content/Intent", "CATEGORY_OPENABLE", "Ljava/lang/String;");
    
                QAndroidJniObject fileTypeStr = QAndroidJniObject::fromString(QString("*/*"));
                intent.callObjectMethod("setType","(Ljava/lang/String;)Landroid/content/Intent;", fileTypeStr.object<jobject>());
                intent.callObjectMethod("addCategory","(Ljava/lang/String;)Landroid/content/Intent;", Intent__CATEGORY_OPENABLE.object<jobject>());
                intent.callObjectMethod("setAction","(Ljava/lang/String;)Landroid/content/Intent;", Intent__ACTION_GET_CONTENT.object<jobject>());
    
                QAndroidJniObject intentChooser = QAndroidJniObject::callStaticObjectMethod("android/content/Intent", "createChooser",
                                                                               "(Landroid/content/Intent;Ljava/lang/CharSequence;)Landroid/content/Intent;",
                                                                               intent.object(), QAndroidJniObject::fromString(QString("Select file to import")).object());
                qDebug() << intentChooser.toString();
                QtAndroid::startActivity(intentChooser, 1001, this);
    
            }
    
        }
    
    
    }
    
    void ImportSettingsRetrieveResultReceiver::handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data) {
    
        qDebug() << "INVOKED" << endl;
    
    }
    

    As I read a lot of topics about my issue, the URI of the file should be caught in "handleActivityResult". Unfortunately, the method is never fired.

    Here are some additional information:
    The launchmode is "singleTop" in the manifest
    Remark: if "singleInstance" is selected, handleActivityResult is firing but with a RESULT_CANCELLED instead of RESULT_OK.
    My platforms are samsung galaxy J5 with android 6 and an ASUS tablet ME302C with android 4.
    The Qt version is 5.9 for android. The interface is designed using Qtquick 2 tools.

    Can somebody help to figure this out ?

    Regards,

    1 Reply Last reply
    0
    • S Offline
      S Offline
      Syl1211
      wrote on last edited by
      #2

      I found a workaround using java code.
      I wrote an extension of the "org.qtproject.qt5.android.bindings.QtActivity" class.
      Here is this extension:
      MyAppActivity.java

      public class MyAppActivity extends QtActivity
      {
      
          @Override
          public void onCreate(Bundle savedInstanceState)
          {
              System.out.println("icoms activity created!");
              super.onCreate(savedInstanceState);
      
          }
      
      
          public void fireFileConfigPicker() {
      
      
              // Fonctionne
      
              Intent intent = new Intent();
              intent.addCategory(Intent.CATEGORY_OPENABLE);
              // Set your required file type
              intent.setType("*/*");
              intent.setAction(Intent.ACTION_GET_CONTENT);
              startActivityForResult(Intent.createChooser(intent, "Pick a config file"),1001);
      
          }
      
          @Override
          protected void onActivityResult(int requestCode, int resultCode, Intent data)
          {
      
             super.onActivityResult(requestCode, resultCode, data);
             System.out.println("file picked!");
             System.out.println(requestCode);
             System.out.println(resultCode);
      
             if (resultCode == RESULT_OK){
      
                   Uri dataFile = data.getData();
                   System.out.println(data);
                   String txtPath = dataFile.getPath();
                   System.out.println(txtPath);
                   readConfigLines(dataFile);
      
            }
      
      
          }
      
          public void readConfigLines(Uri filePathName) {
      
              BufferedReader br;
              try {
                  br = new BufferedReader(new InputStreamReader(getContentResolver().openInputStream(filePathName)));
                  String line = null;
                  while ((line = br.readLine()) != null) {
                      System.out.println(line);
                  }
      
                  br.close();
              }
              catch (FileNotFoundException e) {
                  e.printStackTrace();
              }
              catch (IOException e) {
                  e.printStackTrace();
              }
      
          }
      
      
      }
      

      Then from c++ side I call the "fireFileConfigPicker()" method using the following statement:

      QtAndroid::androidActivity().callMethod<void>("fireFileConfigPicker", "()V");
      

      The file storage options chooser then opens and I can pick a file and the "onActivityResult()" method fires as excepted, displaying thus my file content.

      So, this indeed fix my global issue. But as I said this is a workaround. It would be great if I could manage that in c++ side without writing any java code.

      Regarding the c++ side, I notice one thing and I'm wondering if this can be a cause of the "handleActivityResult" not triggering. I tried to write the addresses of those activities using the following statements:

      QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;");
      QAndroidJniObject activity2 = QtAndroid::androidActivity();
      

      Both activities refer to different memory places so, these are different objects. In addition both are valid activities.
      So is it possible that, in c++ side, the startActivity intent is called on a wrong activity object, preventing thus the result handler to be triggered ? Sorry, I'm novice in android and may be this question doesn't mean anything and is only a consequence of my misunderstanding.

      For now, I do not put this post as solved so it let time to anyone feeling concerned to bring other ideas concerning c++ side.

      raven-worxR 1 Reply Last reply
      0
      • S Syl1211

        I found a workaround using java code.
        I wrote an extension of the "org.qtproject.qt5.android.bindings.QtActivity" class.
        Here is this extension:
        MyAppActivity.java

        public class MyAppActivity extends QtActivity
        {
        
            @Override
            public void onCreate(Bundle savedInstanceState)
            {
                System.out.println("icoms activity created!");
                super.onCreate(savedInstanceState);
        
            }
        
        
            public void fireFileConfigPicker() {
        
        
                // Fonctionne
        
                Intent intent = new Intent();
                intent.addCategory(Intent.CATEGORY_OPENABLE);
                // Set your required file type
                intent.setType("*/*");
                intent.setAction(Intent.ACTION_GET_CONTENT);
                startActivityForResult(Intent.createChooser(intent, "Pick a config file"),1001);
        
            }
        
            @Override
            protected void onActivityResult(int requestCode, int resultCode, Intent data)
            {
        
               super.onActivityResult(requestCode, resultCode, data);
               System.out.println("file picked!");
               System.out.println(requestCode);
               System.out.println(resultCode);
        
               if (resultCode == RESULT_OK){
        
                     Uri dataFile = data.getData();
                     System.out.println(data);
                     String txtPath = dataFile.getPath();
                     System.out.println(txtPath);
                     readConfigLines(dataFile);
        
              }
        
        
            }
        
            public void readConfigLines(Uri filePathName) {
        
                BufferedReader br;
                try {
                    br = new BufferedReader(new InputStreamReader(getContentResolver().openInputStream(filePathName)));
                    String line = null;
                    while ((line = br.readLine()) != null) {
                        System.out.println(line);
                    }
        
                    br.close();
                }
                catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
                catch (IOException e) {
                    e.printStackTrace();
                }
        
            }
        
        
        }
        

        Then from c++ side I call the "fireFileConfigPicker()" method using the following statement:

        QtAndroid::androidActivity().callMethod<void>("fireFileConfigPicker", "()V");
        

        The file storage options chooser then opens and I can pick a file and the "onActivityResult()" method fires as excepted, displaying thus my file content.

        So, this indeed fix my global issue. But as I said this is a workaround. It would be great if I could manage that in c++ side without writing any java code.

        Regarding the c++ side, I notice one thing and I'm wondering if this can be a cause of the "handleActivityResult" not triggering. I tried to write the addresses of those activities using the following statements:

        QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;");
        QAndroidJniObject activity2 = QtAndroid::androidActivity();
        

        Both activities refer to different memory places so, these are different objects. In addition both are valid activities.
        So is it possible that, in c++ side, the startActivity intent is called on a wrong activity object, preventing thus the result handler to be triggered ? Sorry, I'm novice in android and may be this question doesn't mean anything and is only a consequence of my misunderstanding.

        For now, I do not put this post as solved so it let time to anyone feeling concerned to bring other ideas concerning c++ side.

        raven-worxR Offline
        raven-worxR Offline
        raven-worx
        Moderators
        wrote on last edited by
        #3

        @Syl1211 said in Android get file URI using JNI objects:

        Both activities refer to different memory places so, these are different objects. In addition both are valid activities.

        how do you check the memory addresses?! o.O
        If you are comparing the memory addresses of the QAndroidJniObject instances this is of course wrong.
        Since JAVA is running in a VM you can't rely on any address/identifier you would gather of an object anyway.

        You could make a backup of the QtActivity java file and add some debug prints there.

        --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
        If you have a question please use the forum so others can benefit from the solution in the future

        S 1 Reply Last reply
        0
        • raven-worxR raven-worx

          @Syl1211 said in Android get file URI using JNI objects:

          Both activities refer to different memory places so, these are different objects. In addition both are valid activities.

          how do you check the memory addresses?! o.O
          If you are comparing the memory addresses of the QAndroidJniObject instances this is of course wrong.
          Since JAVA is running in a VM you can't rely on any address/identifier you would gather of an object anyway.

          You could make a backup of the QtActivity java file and add some debug prints there.

          S Offline
          S Offline
          Syl1211
          wrote on last edited by
          #4

          @raven-worx
          I check the address in the Qt debug console:

          qDebug() << "activity 1 " << &activity << endl;
          qDebug() << "activity 2 " << &activity2 << endl;
          

          It prints (changing on each execution):

          D/libapp_icoms_isafe.so(10249): ... activity 1  0x7a650d88
          D/libapp_icoms_isafe.so(10249):
          D/libapp_icoms_isafe.so(10249): ... activity 2  0x7a650d90
          

          As you said, it makes no sense to do that.

          Regarding a backup of the QtActivity.java and if I well understood what you said, I already tweaked the file by adding a println statement in the "onActivityResult" and nothing happened. For me, I still cannot explain why this work with the java extension and not with the c++ code.

          raven-worxR 1 Reply Last reply
          0
          • S Syl1211

            @raven-worx
            I check the address in the Qt debug console:

            qDebug() << "activity 1 " << &activity << endl;
            qDebug() << "activity 2 " << &activity2 << endl;
            

            It prints (changing on each execution):

            D/libapp_icoms_isafe.so(10249): ... activity 1  0x7a650d88
            D/libapp_icoms_isafe.so(10249):
            D/libapp_icoms_isafe.so(10249): ... activity 2  0x7a650d90
            

            As you said, it makes no sense to do that.

            Regarding a backup of the QtActivity.java and if I well understood what you said, I already tweaked the file by adding a println statement in the "onActivityResult" and nothing happened. For me, I still cannot explain why this work with the java extension and not with the c++ code.

            raven-worxR Offline
            raven-worxR Offline
            raven-worx
            Moderators
            wrote on last edited by
            #5

            @Syl1211
            then your code just prints the memory address of the QAndroidJniObject instance (= "wrapper" around the actual Java object instance) itself which is allocated on the stack. Thus it's different on every execution cycle.

            I haven't used sending Intents with Qt's mechanism yet, so i have no experience with it yet.
            But it's next on my list for QrwAndroid ;)

            --- SUPPORT REQUESTS VIA CHAT WILL BE IGNORED ---
            If you have a question please use the forum so others can benefit from the solution in the future

            S 1 Reply Last reply
            1
            • raven-worxR raven-worx

              @Syl1211
              then your code just prints the memory address of the QAndroidJniObject instance (= "wrapper" around the actual Java object instance) itself which is allocated on the stack. Thus it's different on every execution cycle.

              I haven't used sending Intents with Qt's mechanism yet, so i have no experience with it yet.
              But it's next on my list for QrwAndroid ;)

              S Offline
              S Offline
              Syl1211
              wrote on last edited by
              #6

              @raven-worx
              Ok, If I have time, I will try to search why it doesn't work on c++ side more deeply.
              Thanks for you replies.

              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