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. Trying to get Image url from Android Gallery App, bbut Path is always empty
Forum Updated to NodeBB v4.3 + New Features

Trying to get Image url from Android Gallery App, bbut Path is always empty

Scheduled Pinned Locked Moved Solved Mobile and Embedded
4 Posts 3 Posters 3.8k Views 2 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.
  • A Offline
    A Offline
    Antonio Ortiz
    wrote on 10 Jan 2018, 19:34 last edited by
    #1

    Hi, I'm trying to get an image url from the Android Gallery, but the path is always empty.

    To open the gallery I make an Intent to the app using the following method:

    void DialogoArchivoAndroid::abrir()
    {
        QAndroidJniObject ACTION_PICK = QAndroidJniObject::fromString("android.intent.action.GET_CONTENT");
        QAndroidJniObject intent("android/content/Intent");
        if (ACTION_PICK.isValid() && intent.isValid())
        {
            intent.callObjectMethod("setAction", "(Ljava/lang/String;)Landroid/content/Intent;", ACTION_PICK.object<jstring>());
            intent.callObjectMethod("setType", "(Ljava/lang/String;)Landroid/content/Intent;", QAndroidJniObject::fromString("image/*").object<jstring>());
            QtAndroid::startActivity(intent.object<jobject>(), 101, this);
        }
    }
    

    Once I pick the image the Gallery close an my app returns to foreground and receive data using the handleActivityResult and store the image url in a class attribute name "archivoSeleccionado":

    void DialogoArchivoAndroid::handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data)
    {
        jint RESULT_OK = QAndroidJniObject::getStaticField<jint>("android/app/Activity", "RESULT_OK");
    
        if (receiverRequestCode == 101 && resultCode == RESULT_OK)
        {
    
    
            QAndroidJniObject uri = data.callObjectMethod("getData", "()Landroid/net/Uri;");
    
            QAndroidJniObject datosAndroid = QAndroidJniObject::getStaticObjectField("android/provider/MediaStore$MediaColumns",
                                                                                     "DATA", "Ljava/lang/String;");
            QAndroidJniEnvironment env;
            jobjectArray proyeccion = (jobjectArray)env->NewObjectArray(1, env->FindClass("java/lang/String"), NULL);
            jobject proyeccionDatosAndroid = env->NewStringUTF(datosAndroid.toString().toStdString().c_str());
            env->SetObjectArrayElement(proyeccion, 0, proyeccionDatosAndroid);
            QAndroidJniObject contentResolver = QtAndroid::androidActivity().callObjectMethod("getContentResolver", "()Landroid/content/ContentResolver;");
            QAndroidJniObject cursor = contentResolver.callObjectMethod("query",
                                                                        "(Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;",
                                                                        uri.object<jobject>(), proyeccion, NULL, NULL, NULL);
            jint columnIndex = cursor.callMethod<jint>("getColumnIndex", "(Ljava/lang/String;)I", datosAndroid.object<jstring>());
            cursor.callMethod<jboolean>("moveToFirst", "()Z");
            QAndroidJniObject resultado = cursor.callObjectMethod("getString", "(I)Ljava/lang/String;", columnIndex);
    
            archivoSeleccionado = "file://" + resultado.toString();
            emit archivoSeleccionadoCambio();
        }
    

    Debugging the app I found that RESULT_OK and result code are always -1, but I don't understand why.
    I get the code from this thread in the forum.
    This is the first time I use the QAndroidActivityResultReceiver and I'm i little lost.

    1 Reply Last reply
    0
    • L Offline
      L Offline
      Leonardo
      wrote on 11 Jan 2018, 01:24 last edited by
      #2

      Using JNI for complex operations is hard work. I think it would be better for you to extend Qt's activity and do your thing using only Java. I have a similar code for picking images. Here's my suggestion:

      1. Create a "src" folder inside "android" and add a new .java file for your custom activity. From my project:

      0_1515632508206_7325c7e9-288a-45aa-8cc9-bf5dfbddf109-image.png

      1. Here's a sample code for the activity:
      package my.app;
      
      public class MyActivity extends org.qtproject.qt5.android.bindings.QtActivity {
      
          private static int REQUEST_IMAGE_PICK = 101;
      
          public static native void photoResult(String filePath);
      
          @Override
          protected void onActivityResult(int requestCode, int resultCode, Intent data) {
      
              super.onActivityResult(requestCode, resultCode, data);
      
              if(requestCode == REQUEST_IMAGE_PICK && resultCode == MyActivity.RESULT_OK){
                  this.handleImagePickResult(data);
              }
      
          }
      
          private void handleImagePickResult(Intent data){
      
              try {
      
                  android.net.Uri photoUri = data.getData();
                  java.io.File photoFile = this.createImageFile();
                  byte[] buf = new byte[10240];
                  int count;
      
                  java.io.InputStream input = this.getContentResolver().openInputStream(photoUri);
                  java.io.FileOutputStream output = new java.io.FileOutputStream(photoFile);
      
                  do {
      
                      count = input.read(buf);
      
                      if(count > 0){
                          output.write(buf, 0, count);
                      }else{
                          break;
                      }
      
                  }while(true);
      
                  output.close();
                  input.close();
      
                  photoResult(photoFile.getAbsolutePath());
      
              }catch(java.lang.Exception ex){
                  photoResult("");
              }
      
          }
              
          private java.io.File createImageFile(){
      
              java.io.File file;
      
              try {
                  file = java.io.File.createTempFile("MY_", ".jpg", this.getCacheDir());
              }catch(java.lang.Exception ex){
                  return null;
              }
      
              return file;
      
          }
          
          public void photoFromGallery(){
      
              Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
      
              intent.setType("image/*");
              intent.addCategory(Intent.CATEGORY_OPENABLE);
              intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
      
              if(intent.resolveActivity(getPackageManager()) != null){
                  startActivityForResult(intent, REQUEST_IMAGE_PICK);
              }else{
                  photoResult("");
              }
      
          }
          
      }
      
      1. On your manifest, change the "android:name" of your <activity> to "my.app.MyActivity" and add the following inside <application>:
      <provider
      	 tools:replace="android:authorities"
      	 android:name="android.support.v4.content.FileProvider"
      	 android:authorities="my.app.files"
      	 android:exported="false"
      	 android:grantUriPermissions="true">
      	 <meta-data
      		 android:name="android.support.FILE_PROVIDER_PATHS"
      		 android:resource="@xml/file_paths"></meta-data>
       </provider>
      
      1. Create a "res/xml/file_paths.xml" file with this content:
      <?xml version="1.0" encoding="utf-8"?>
      <paths xmlns:android="http://schemas.android.com/apk/res/android">
          <cache-path name="photos" path="" />
          <external-path name="dcim" path="DCIM"/>
      </paths>
      
      1. Now you just need to implement the "callback" from Java to Qt. When initializing your app, bind it to a C++ function.
      JNINativeMethod methods[] = {{"photoResult", "(Ljava/lang/String;)V", reinterpret_cast<void *>(MyPhotoResult)}};
      QAndroidJniEnvironment env;
      
      jclass objectClass = env->GetObjectClass(QtAndroid::androidActivity().object());
      
      env->RegisterNatives(objectClass, methods, sizeof(methods) / sizeof(methods[0]));
      
      env->DeleteLocalRef(objectClass);
      
      void MyPhotoResult(JNIEnv *env, jobject obj, jstring jdata){
      	QString filePath = QAndroidJniObject(jdata).toString();
      	// do your thing
      }
      

      I don't remember exactly why, but as you see I had to copy the file to a place where Qt could manipulate it. I couldn't access the file directly. Maybe you can work around that, I don't know, but it works as it is.

      1 Reply Last reply
      0
      • A Offline
        A Offline
        Antonio Ortiz
        wrote on 22 Jan 2018, 14:22 last edited by
        #3

        Hi, thanks for your quick answer (and sorry for my late one). I use your code as base and still couldn't get the file path. But I investigate a like more and find out that it was a problem with my Android version, for Android >= 5.1 things change a little bit.
        I found the answer in the next post:
        https://stackoverflow.com/questions/34006608/qt-and-android-get-path-from-image-in-gallery
        https://stackoverflow.com/questions/30567217/android-5-1-1-lollipop-return-null-file-path-if-image-chosen-from-gallery
        https://forum.qt.io/topic/66324/qt-android-image-picker-issue-with-android-5-5-1/3

        And this is my final code:
        class.h

        #ifndef DIALOGOARCHIVOANDROID_HPP
        #define DIALOGOARCHIVOANDROID_HPP
        
        #include <QObject>
        
        #ifdef Q_OS_ANDROID
            #include <QtAndroidExtras>
            #include <QAndroidActivityResultReceiver>
            #include <QAndroidJniObject>
            #include <QAndroidJniEnvironment>
        #endif
        
        #ifdef Q_OS_ANDROID
        class DialogoArchivoAndroid : public QObject, QAndroidActivityResultReceiver
        #else
        class DialogoArchivoAndroid : public QObject
        #endif
        {
            Q_OBJECT
        
            Q_PROPERTY(QString archivoSeleccionado READ getArchivoSeleccionado WRITE setArchivoSeleccionado NOTIFY archivoSeleccionadoCambio)
        
            QString archivoSeleccionado;
        
        public:
            explicit DialogoArchivoAndroid(QObject *parent = 0);
        
            QString getArchivoSeleccionado() const;
            void setArchivoSeleccionado(const QString &value);
        
        
        
        #ifdef Q_OS_ANDROID
            Q_INVOKABLE void abrir();
            virtual void handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject & data) override;
        #endif
            Q_INVOKABLE bool verificarSO();
        
        
        signals:
        
            void archivoSeleccionadoCambio();
        
        public slots:
        };
        
        #endif // DIALOGOARCHIVOANDROID_HPP
        
        

        Class.cpp

        #include "dialogoarchivoandroid.hpp"
        
        DialogoArchivoAndroid::DialogoArchivoAndroid(QObject *parent) : QObject(parent)
        {
        
        }
        
        QString DialogoArchivoAndroid::getArchivoSeleccionado() const
        {
            return archivoSeleccionado;
        }
        
        bool DialogoArchivoAndroid::verificarSO()
        {
        #ifdef Q_OS_ANDROID
            return true;
        #endif
            return false;
        }
        
        void DialogoArchivoAndroid::setArchivoSeleccionado(const QString &value)
        {
            archivoSeleccionado = value;
        }
        
        
        #ifdef Q_OS_ANDROID
        
        
        void DialogoArchivoAndroid::abrir()
        {
            QAndroidJniObject Intent__ACTION_PICK = QAndroidJniObject::getStaticObjectField("android/content/Intent", "ACTION_PICK", "Ljava/lang/String;");
        
            QAndroidJniObject EXTERNAL_CONTENT_URI= QAndroidJniObject::getStaticObjectField("android/provider/MediaStore$Images$Media", "EXTERNAL_CONTENT_URI", "Landroid/net/Uri;");
        
            QAndroidJniObject intent=QAndroidJniObject("android/content/Intent",
                                                       "(Ljava/lang/String;Landroid/net/Uri;)V",
                                                       Intent__ACTION_PICK.object<jstring>(),
                                                       EXTERNAL_CONTENT_URI.object<jobject>()
                                                       );
        
            QtAndroid::startActivity(intent, 101, this);
        }
        
        void DialogoArchivoAndroid::handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data)
        {
            jint RESULT_OK = QAndroidJniObject::getStaticField<jint>("android/app/Activity", "RESULT_OK");
        
            if (receiverRequestCode == 101 && resultCode == RESULT_OK)
            {
        
                QAndroidJniObject uri = data.callObjectMethod("getData", "()Landroid/net/Uri;");
        
                QAndroidJniObject datosAndroid = QAndroidJniObject::getStaticObjectField("android/provider/MediaStore$MediaColumns", "DATA", "Ljava/lang/String;");
                QAndroidJniEnvironment env;
                jobjectArray proyeccion = (jobjectArray)env->NewObjectArray(1, env->FindClass("java/lang/String"), NULL);
                jobject proyeccionDatosAndroid = env->NewStringUTF(datosAndroid.toString().toStdString().c_str());
                env->SetObjectArrayElement(proyeccion, 0, proyeccionDatosAndroid);
        
                QAndroidJniObject contentResolver = QtAndroid::androidActivity().callObjectMethod("getContentResolver", "()Landroid/content/ContentResolver;");
                QAndroidJniObject nullObj;
        
                QAndroidJniObject cursor = contentResolver.callObjectMethod("query", "(Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;", uri.object<jobject>(), proyeccion, nullObj.object<jstring>(), nullObj.object<jobjectArray>(), nullObj.object<jstring>());
               
                jint columnIndex = cursor.callMethod<jint>("getColumnIndexOrThrow","(Ljava/lang/String;)I", datosAndroid.object<jstring>());
              
                cursor.callMethod<jboolean>("moveToFirst");
        
                QAndroidJniObject path = cursor.callObjectMethod("getString", "(I)Ljava/lang/String;", columnIndex);
                
                archivoSeleccionado = "file://" +  path.toString();
        
                cursor.callMethod<jboolean>("close");
                emit archivoSeleccionadoCambio();
            }
        }
        
        #endif
        
        ekkescornerE 1 Reply Last reply 22 Jan 2018, 14:38
        0
        • A Antonio Ortiz
          22 Jan 2018, 14:22

          Hi, thanks for your quick answer (and sorry for my late one). I use your code as base and still couldn't get the file path. But I investigate a like more and find out that it was a problem with my Android version, for Android >= 5.1 things change a little bit.
          I found the answer in the next post:
          https://stackoverflow.com/questions/34006608/qt-and-android-get-path-from-image-in-gallery
          https://stackoverflow.com/questions/30567217/android-5-1-1-lollipop-return-null-file-path-if-image-chosen-from-gallery
          https://forum.qt.io/topic/66324/qt-android-image-picker-issue-with-android-5-5-1/3

          And this is my final code:
          class.h

          #ifndef DIALOGOARCHIVOANDROID_HPP
          #define DIALOGOARCHIVOANDROID_HPP
          
          #include <QObject>
          
          #ifdef Q_OS_ANDROID
              #include <QtAndroidExtras>
              #include <QAndroidActivityResultReceiver>
              #include <QAndroidJniObject>
              #include <QAndroidJniEnvironment>
          #endif
          
          #ifdef Q_OS_ANDROID
          class DialogoArchivoAndroid : public QObject, QAndroidActivityResultReceiver
          #else
          class DialogoArchivoAndroid : public QObject
          #endif
          {
              Q_OBJECT
          
              Q_PROPERTY(QString archivoSeleccionado READ getArchivoSeleccionado WRITE setArchivoSeleccionado NOTIFY archivoSeleccionadoCambio)
          
              QString archivoSeleccionado;
          
          public:
              explicit DialogoArchivoAndroid(QObject *parent = 0);
          
              QString getArchivoSeleccionado() const;
              void setArchivoSeleccionado(const QString &value);
          
          
          
          #ifdef Q_OS_ANDROID
              Q_INVOKABLE void abrir();
              virtual void handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject & data) override;
          #endif
              Q_INVOKABLE bool verificarSO();
          
          
          signals:
          
              void archivoSeleccionadoCambio();
          
          public slots:
          };
          
          #endif // DIALOGOARCHIVOANDROID_HPP
          
          

          Class.cpp

          #include "dialogoarchivoandroid.hpp"
          
          DialogoArchivoAndroid::DialogoArchivoAndroid(QObject *parent) : QObject(parent)
          {
          
          }
          
          QString DialogoArchivoAndroid::getArchivoSeleccionado() const
          {
              return archivoSeleccionado;
          }
          
          bool DialogoArchivoAndroid::verificarSO()
          {
          #ifdef Q_OS_ANDROID
              return true;
          #endif
              return false;
          }
          
          void DialogoArchivoAndroid::setArchivoSeleccionado(const QString &value)
          {
              archivoSeleccionado = value;
          }
          
          
          #ifdef Q_OS_ANDROID
          
          
          void DialogoArchivoAndroid::abrir()
          {
              QAndroidJniObject Intent__ACTION_PICK = QAndroidJniObject::getStaticObjectField("android/content/Intent", "ACTION_PICK", "Ljava/lang/String;");
          
              QAndroidJniObject EXTERNAL_CONTENT_URI= QAndroidJniObject::getStaticObjectField("android/provider/MediaStore$Images$Media", "EXTERNAL_CONTENT_URI", "Landroid/net/Uri;");
          
              QAndroidJniObject intent=QAndroidJniObject("android/content/Intent",
                                                         "(Ljava/lang/String;Landroid/net/Uri;)V",
                                                         Intent__ACTION_PICK.object<jstring>(),
                                                         EXTERNAL_CONTENT_URI.object<jobject>()
                                                         );
          
              QtAndroid::startActivity(intent, 101, this);
          }
          
          void DialogoArchivoAndroid::handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject &data)
          {
              jint RESULT_OK = QAndroidJniObject::getStaticField<jint>("android/app/Activity", "RESULT_OK");
          
              if (receiverRequestCode == 101 && resultCode == RESULT_OK)
              {
          
                  QAndroidJniObject uri = data.callObjectMethod("getData", "()Landroid/net/Uri;");
          
                  QAndroidJniObject datosAndroid = QAndroidJniObject::getStaticObjectField("android/provider/MediaStore$MediaColumns", "DATA", "Ljava/lang/String;");
                  QAndroidJniEnvironment env;
                  jobjectArray proyeccion = (jobjectArray)env->NewObjectArray(1, env->FindClass("java/lang/String"), NULL);
                  jobject proyeccionDatosAndroid = env->NewStringUTF(datosAndroid.toString().toStdString().c_str());
                  env->SetObjectArrayElement(proyeccion, 0, proyeccionDatosAndroid);
          
                  QAndroidJniObject contentResolver = QtAndroid::androidActivity().callObjectMethod("getContentResolver", "()Landroid/content/ContentResolver;");
                  QAndroidJniObject nullObj;
          
                  QAndroidJniObject cursor = contentResolver.callObjectMethod("query", "(Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;", uri.object<jobject>(), proyeccion, nullObj.object<jstring>(), nullObj.object<jobjectArray>(), nullObj.object<jstring>());
                 
                  jint columnIndex = cursor.callMethod<jint>("getColumnIndexOrThrow","(Ljava/lang/String;)I", datosAndroid.object<jstring>());
                
                  cursor.callMethod<jboolean>("moveToFirst");
          
                  QAndroidJniObject path = cursor.callObjectMethod("getString", "(I)Ljava/lang/String;", columnIndex);
                  
                  archivoSeleccionado = "file://" +  path.toString();
          
                  cursor.callMethod<jboolean>("close");
                  emit archivoSeleccionadoCambio();
              }
          }
          
          #endif
          
          ekkescornerE Offline
          ekkescornerE Offline
          ekkescorner
          Qt Champions 2016
          wrote on 22 Jan 2018, 14:38 last edited by
          #4

          @Antonio-Ortiz Hi Antonio,
          you can also take a look at my Share Example App where I'm also dealing with Android content pathes and to see HowTo use JAVA instead of JNI - much easier to write.
          see my blog at Qt: http://blog.qt.io/blog/2018/01/16/sharing-files-android-ios-qt-app-part-2/
          and esp this JAVA class to get the FilePath from ContentProvider: https://github.com/ekke/ekkesSHAREexample/blob/master/android/src/org/ekkescorner/utils/QSharePathResolver.java

          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