Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Android auto update



  • Hi!

    I want to make an automatic update solution on Android, the apk download works, but I can't start it. I found this solution for it:

    QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("org/qtproject/qt5/android/QtNative", "activity", "()Landroid/app/Activity;");
                if (activity.isValid())
                {
                    QAndroidJniObject kindOfActivity    = QAndroidJniObject::fromString("android.intent.action.VIEW");
                    QAndroidJniObject apkFile           = QAndroidJniObject::fromString(QString("file://%1/latest.apk").arg(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)));
                    QAndroidJniObject apkUri            = QAndroidJniObject::callStaticObjectMethod("android/net/Uri", "parse", "(Ljava/lang/String;)Landroid/net/Uri;", apkFile.object());
                    QAndroidJniObject mimetype          = QAndroidJniObject::fromString("application/vnd.android.package-archive");
    
                    QAndroidJniObject intent("android/content/Intent", "(Ljava/lang/String;)V", kindOfActivity.object());
    
                    intent = intent.callObjectMethod("setDataAndType", "(Landroid/net/Uri;Ljava/lang/String;)Landroid/content/Intent;", apkUri.object(), mimetype.object());
                    intent = intent.callObjectMethod("setFlags", "(I)Landroid/content/Intent;", FLAG_ACTIVITY_NEW_TASK);
                    activity.callMethod<void>("startActivity", "(Landroid/content/Intent;)V", intent.object());
                }
    
                QApplication::exit();
    

    But gives error message:

    JNI DETECTED ERROR IN APPLICATION: JNI GetStaticMethodID called with pending exception android.os.FileUriExposedException: file: ///storage/emulated/0/Download/latest.apk exposed beyond app through Intent.getData ()
    

    Thanks in advance for the answers.


  • Moderators

    You need to process the URI through FileProvider. It's an awfully awkward and tedious thing, but Android requires it since version 6 IIRC.

    Basically you need to:

    1. Add file provider to res/xml/fileprovider.xml. Example contents:
    <?xml version="1.0" encoding="utf-8"?>
    <paths>
        <cache-path name="exports" path="." />
        <!--Uncomment below to share the entire application specific directory -->
        <!--<external-path name="all_dirs" path="."/>-->
    </paths>
    
    1. Register that provider in your manifest:
    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="your.application.domain.fileprovider"
        android:exported="false"
        android:grantUriPermissions="true">
          <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/fileprovider" />
        </provider>
    
    1. In every place in Java you need to use the file provider to "access" that file, instead of regular File/ Intent. I don't have Qt code for this, only some Java (and written for different purpose so it might be incorrect for your case):
    import android.support.v4.content.FileProvider;
    // ...
    Uri providerUri = FileProvider.getUriForFile(
                    this,
                    "your.application.domain.fileprovider",
                    fileName);
    

    You probably need to do the opposite (get file for URI). Not sure. I wrote this ages ago and APIs in Android keep changing all the time :-(

    1. You may also need to add the v4 compat library in gradle config, I'm not sure. It could be something like this:
    dependencies {
        implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
        compile 'com.android.support:support-v4:23.+'
    }
    

    I hope this will help you at least a bit. And sorry for not being more specific, I'm not sure about any of it :D This won't be easy, good luck!



  • @sierdzio
    Thanks for the reply, it's a little complicated for me, I absolutely don't understand the java, maybe I'll try it later.


  • Qt Champions 2016

    @Kutyus my share example has implemented Android FileProvider https://github.com/ekke/ekkesSHAREexample



  • @ekkescorner said in Android auto update:

    @Kutyus my share example has implemented Android FileProvider https://github.com/ekke/ekkesSHAREexample

    Great example.
    Got it to work immediately with Qt5.12.7, but failure with Qt5.14.2.
    Are you interested to get a bug report? And when where?


  • Qt Champions 2016

    @koahnig said in Android auto update:

    Got it to work immediately with Qt5.12.7, but failure with Qt5.14.2.
    Are you interested to get a bug report? And when where?

    please report at gihub issues https://github.com/ekke/ekkesSHAREexample/issues

    unfortunately most of my examples are 3 years or more old and last 2 years I had to develop so many mobile business apps, so I didn't found enough spare time

    the good thing: starting with Qt 5.15 I'll rework all my examples and blogs to get mobile apps ready for Qt6 / strict QML 3. will reduce work for customers next months and concentrate on 5.15 / 6.0. with some luck all of this will finally end in a book (fingers crossed)



  • @ekkescorner

    Thanks. Report created


  • Qt Champions 2016

    @koahnig thx reporting. will test when I've done my switch from 5.13 --> 5.15



  • @sierdzio
    Hi!
    I made the settings, but on this line, there is always an error:

    QAndroidJniObject uri       = QAndroidJniObject::callStaticObjectMethod("android.support.v4.content.FileProvider", "FileProvider.getUriForFile", "(Landroid/content/Context;Ljava/lang/String;Ljava.io.File;)Landroid/net/Uri;", activity.object(), authority.object(), file.object());
    
    
    System.err: java.lang.NoSuchMethodError: no static method "Landroid/support/v4/content/FileProvider;.FileProvider.getUriForFile(Landroid/content/Context;Ljava/lang/String;Ljava.io.File;)Landroid/net/Uri;"
    

    I also tried the getUriForFile method without the FileProvider prefix, the same error anyway.


  • Moderators

    I don't know JNI enough to answer, for me it's always a bunch of trial and error to get it to work :D



  • @Kutyus Your JNI call is not correct, you pass wrong method name!
    It should be:

    QAndroidJniObject uri = QAndroidJniObject::callStaticObjectMethod(
            "android.support.v4.content.FileProvider", 
            "getUriForFile",  // only the function name!!
            "(Landroid/content/Context;Ljava/lang/String;Ljava.io.File;)Landroid/net/Uri;",
            activity.object(), 
            authority.object(), 
            file.object());
    
    


  • @KroMignon said in Android auto update:
    I tried anyway. The error in the second parameter was slashes instead of periods in the java.io.File section.
    If I log out after the call, I get an error that there is no activity handling the intent, if I don't log out immediately, there is no error, but nothing happens. I don't know where the error might be.



  • @Kutyus You are right, there are many typos!

    QAndroidJniObject uri = QAndroidJniObject::callStaticObjectMethod(
            "android/support/v4/content/FileProvider", 
            "getUriForFile",  // only the function name!!
            "(Landroid/content/Context;Ljava/lang/String;Ljava/io/File;)Landroid/net/Uri;",
            activity.object(), 
            authority.object(), 
            file.object());
    

Log in to reply