5

I am developing a Qt-based application for Android. In case the app is launched as a result of opening a file I need to pass it to the Qt application instance. I have created a custom Java activity so as to implement this behavior:

public class MyActivity extends org.qtproject.qt5.android.bindings.QtActivity
{
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);       

        Intent i = getIntent();
        String act = i.getAction();

        if (act != null) && act.compareTo(Intent.ACTION_VIEW) == 0) 
        {             
                int fd = -1;
                try
                {
                    fd = getContentResolver().openFileDescriptor(i.getData(), "r").detachFd();
                }
                catch (IOException e)
                {
                    Log.e("MyActivity::onCreate", "Exception caught: " + e.getMessage());
                }

                boolean ok = openFd(fd);            
        }
    }   // onCreate

    private static native boolean openFd(int fd);
} // MyActivity

On the C++ side I have something like this:

#include <QDebug>

#include <jni.h>

jboolean processInputFile(JNIEnv *env, jobject obj, jint fileDescriptor)
{
    int fd = (int)fileDescriptor;

    qDebug() << "processInputFile called: " << fd;

    QFile f;
    if (f.open(fd, QIODevice::ReadOnly, QFileDevice::AutoCloseHandle))
    {
        QFileInfo fi(f);
        qDebug() << "Successfully opened the file " << f.fileName() << fi.absoluteFilePath() << fi.canonicalFilePath() << fi.fileName();
        return true;
    }
    else
    {
        qDebug() << "Failed to open the file: " << f.errorString();
        return false;
    }
}   // processInputFile

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*)
{
    qDebug() << "JNI_OnLoad got called";

    JNIEnv* env;
    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
        return -1;
    }

    jclass javaClass = env->FindClass("com/mycompany/mypackage/MyActivity");
    if(!javaClass)
    {
        return JNI_ERR;
    }

    // Register methods with env->RegisterNatives

    static JNINativeMethod methodsArray[] =
    {
        {"openFd", "(I)Z", (void*)processInputFile}        
    };

    if(env->RegisterNatives(javaClass, methodsArray, sizeof(methodsArray) / sizeof(methodsArray[0])) < 0)
    {
        return JNI_ERR;
    }

    return JNI_VERSION_1_6;
}

int main(int argc, char *argv[])
{
    qDebug() << "Calling main()";

    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QGuiApplication app(argc, argv);

    qDebug() << "main arguments:";
    for (int i = 0; i < argc; ++i)
        qDebug() << argv[i];

    // ...
}

The C++ method (processInputFile) gets invoked, but it happens before the main() function is called, therefore the application object has not been created yet. I can't handle the data passed in, because the processing classes do not exist at that moment. So my question is how can I call the methods of Qt application instance from android.app.Activity::onCreate method?

And another thing: is it possible to get the name of the file that was opened? Sometimes the intent's URI looks like "content://downloads/my_downloads/47" and neither conversion method I found worked for it. QFile and QFileInfo also do not return the name for fd. Knowing the name would be useful, for instance, to describe the item that is being processed, whereas 47 is not really meaningful.

1 Answer 1

1

I think I found a solution for this problem. It is a workaround but should work fine.

In the onCreate function store the filePath and the ncall it from Qt main loop:

public class MyActivity extends org.qtproject.qt5.android.bindings.QtActivity

{ String filePath;

@Override
public void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);       

    Intent i = getIntent();
    String act = i.getAction();
    filePath = "";

    if (act != null) && act.compareTo(Intent.ACTION_VIEW) == 0) 
    {             
            filePath = i.getData();
    }
}   // onCreate

    public String getFile()
    {
        return filePath;
    }

} // MyActivity

Then in your main() function in C++ call

QAndroidJniObject javaFilePath =  QtAndroid::androidActivity().callObjectMethod("getFile", "()Ljava/lang/String;");
QString filePath = javaFilePath.toString();
if (filePath != "") {
    // do something
}

I just tried it and it seems to work. It might need some more work to handle all cases but as a basic idea it seems to do the job.

Another possibility is to use pending intents as described here. It also contains a solution to convert file URI to the real path.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.