web-dev-qa-db-fra.com

Android installer apk avec Intent.VIEW_ACTION ne fonctionne pas avec le fournisseur de fichier

Mon application dispose d'une fonctionnalité de mise à jour automatique permettant de télécharger un APK et, une fois le téléchargement terminé, d'intent.VIEW_ACTION pour ouvrir l'application et permettre à l'utilisateur d'installer l'apk téléchargé

         Uri uri = Uri.parse("file://" + destination);
         Intent install = new Intent(Intent.ACTION_VIEW);
        install.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        install.setDataAndType(uri,
            manager.getMimeTypeForDownloadedFile(downloadId));
        activity.startActivity(install);

Cela fonctionne très bien pour tout le périphérique <24

Maintenant, avec Android 24, il ne nous est apparemment plus permis de démarrer l'intention avec file: /// et après quelques recherches sur Google, il a été conseillé d'utiliser un fournisseur de fichiers

nouveau code:

Intent install = new Intent(Intent.ACTION_VIEW);
    install.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    install.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    Uri apkUri = FileProvider.getUriForFile(AutoUpdate.this,
        BuildConfig.APPLICATION_ID + ".provider", file);
    install.setDataAndType(apkUri,
        manager.getMimeTypeForDownloadedFile(downloadId));
    activity.startActivity(install);

Maintenant, activity.startActivity (install); jette une erreur

Aucune activité trouvée pour gérer l'intention {act = Android.intent.action.VIEW dat = content: //com.xxxx.xx.provider/MyFolder/Download/MyApkFile.apk typ = application/vnd.Android.package-archive flg = 0x4000000}

Existe-t-il un moyen d’ouvrir le lecteur APK dans Android 7 (24)?

42
Johny19

Après beaucoup d'essais, j'ai pu résoudre ce problème en créant différentes intentions pour toutes les versions inférieures à Nougat, telles qu'utiliser FileProvider pour créer une intention d'installation avec les versions Android avant que Nougat ne provoque l'erreur:

ActivityNotFoundException: No Activity found to handle Intent { act=Android.intent.action.INSTALL_PACKAGE dat=content://XXX.apk flg=0x1 }

Lors de l'utilisation d'un Uri normal sur Android, Nougat crée l'erreur suivante:

FileUriExposedException: file:///XXX.apk exposed beyond app through Intent.getData()

Ma solution qui fonctionne pour moi avec Android N sur l'émulateur et un téléphone fonctionnant sous Android M. 

File toInstall = new File(appDirectory, appName + ".apk");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    Uri apkUri = FileProvider.getUriForFile(activity, BuildConfig.APPLICATION_ID + ".provider", toInstall);
    Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
    intent.setData(apkUri);
    intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    activity.startActivity(intent)
} else {
    Uri apkUri = Uri.fromFile(toInstall);
    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.setDataAndType(apkUri, "application/vnd.Android.package-archive");
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    activity.startActivity(intent);
}

MISE À JOUR POUR Android Nougat 7.1:

Vous devez également ajouter l'autorisation REQUEST_INSTALL_PACKAGES dans votre manifeste. Son disponible à partir de Api niveau 23 (Android 6.0 Marshmallow) et requis à partir de niveau 25 (Android 7.1 Nougat).

METTRE À JOUR:

N'oubliez pas de demander les autorisations de lecture et d'écriture sur le stockage externe si le fichier que vous essayez d'installer se trouve sur le stockage externe. Et aussi pour configurer un FileProvider correct pour Android Nougat et au-dessus.

Commencez par vérifier si vous avez l'autorisation d'écrire en appelant canReadWriteExternal() ci-dessous, sinon appelez requestPermission() avant:

private static final int REQUEST_WRITE_PERMISSION = 786;

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    if (requestCode == REQUEST_WRITE_PERMISSION && grantResults[0] == PackageManager.PERMISSION_GRANTED)
        Toast.makeText(this, "Permission granted", Toast.LENGTH_LONG).show();
}

private void requestPermission() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
        requestPermissions(new String[]{ Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_WRITE_PERMISSION);
}

private boolean canReadWriteExternal() {
    return Build.VERSION.SDK_INT < Build.VERSION_CODES.M ||
            ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED;
}

Voici un exemple de fournisseur de fichier pour le dossier de téléchargement sur le stockage externe. AndroidManifest.xml :

<application ... >
    ...

    <provider
        Android:name="Android.support.v4.content.FileProvider"
        Android:authorities="${applicationId}.fileprovider"
        Android:exported="false"
        Android:grantUriPermissions="true">
        <meta-data
            Android:name="Android.support.FILE_PROVIDER_PATHS"
            Android:resource="@xml/filepaths" />
    </provider>
</application>

resources/xml/filepaths.xml :

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:Android="http://schemas.Android.com/apk/res/Android">
    <external-path name="external_download" path="Download"/>
</paths>

Si vous obtenez une erreur lors de l'installation du fichier .apk, par exemple "Il y a un problème d'analyse du package". il se peut que vous n'ayez pas demandé l'autorisation de lecture/écriture ou que le fichier que vous essayez d'installer n'existe pas ou soit corrompu. 

99
just_user

Cela pourrait être le problème, vous avez

intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 

dans votre exemple, il devrait être

install.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

as install est le nom de l'intention.

4
Rob Francis

J'ai eu ce problème lorsque j'ai appelé l'activité de démarrage. Après avoir mis mon activité en pause, elle est soudainement revenue et a appelé OnResume. comme rien ne s'est passé. mon problème était avec cette permission dans manifeste:

 <uses-permission Android:name="Android.permission.REQUEST_INSTALL_PACKAGES" />

presque personne n'en a parlé. alors rappelez-vous ceci. dans sdk> = 24, vous devez utiliser provider car il faut une intention commençant par fichier: /// sous sdk 24, vous devez donner à uri commençant par content: ///, c’est pourquoi nous avons besoin du fournisseur de fichiers pour sdk 24 et versions ultérieures. Je ne pense pas avoir besoin d'écrire de code pour cela car @just_user a écrit la bonne réponse.

1
Hossein Karami

Notez que pour les API <24, vous devez utiliser:

        setDataAndType(Uri.fromFile(apkFile), "application/vnd.Android.package-archive")

au lieu de définir des données et de taper séparément:

data = Uri.fromFile(apkFile)
    type = "application/vnd.Android.package-archive"

sinon, vous obtiendrez ActivityNotFoundException

0
MohammadReza