Après de nombreux essais, j'ai réussi à résoudre ce problème en créant des Intent différents pour tout ce qui est inférieur à Nougat, car l'utilisation de FileProvider pour créer un intent d'installation avec des versions d'Android antérieures à Nougat provoque l'erreur :
ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.INSTALL_PACKAGE dat=content://XXX.apk flg=0x1 }
Alors qu'utiliser une Uri normale 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 sous Android M.
Fichier à installer = nouveau Fichier(dossierApp, nomApp + ".apk");
Intent intent;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Uri apkUri = FileProvider.getUriForFile(activity, BuildConfig.APPLICATION_ID + ".fileprovider", toInstall);
intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.setData(apkUri);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
} else {
Uri apkUri = Uri.fromFile(toInstall);
intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
activity.startActivity(intent);
MAJ pour Android Nougat 7.1 :
Vous devez également ajouter l'autorisation REQUEST_INSTALL_PACKAGES dans votre manifeste. Elle est disponible à partir du niveau API 23 (Android 6.0 Marshmallow) et requise à partir du niveau 25 (Android 7.1 Nougat).
MAJ :
N'oubliez pas de demander les autorisations de lecture et d'écriture sur le stockage externe si le fichier que vous essayez d'installer est sur le stockage externe. Et aussi de configurer un FileProvider correct pour Android Nougat et versions supérieures.
Vérifiez d'abord si vous avez l'autorisation d'écriture 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, "Autorisation accordée", 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 fichiers pour le dossier Téléchargement sur le stockage externe. AndroidManifest.xml:
...
resources/xml/filepaths.xml:
Si vous obtenez une erreur lors de l'installation du fichier .apk disant quelque chose comme "There is a problem parsing the package.", cela pourrait être que vous n'avez pas demandé l'autorisation de lecture/écriture ou que le fichier que vous essayez d'installer n'existe pas ou est corrompu.
MAJ pour Android Oreo 8.0 :
Vous devez vérifier si l'application actuelle est autorisée à installer l'APK sur Android Oreo 8.0 ou supérieur.
Vous pouvez vérifier si votre application est autorisée à installer des APK en utilisant la méthode [canRequestPackageInstalls](https://developer.android.com/reference/android/content/pm/PackageManager#canRequestPackageInstalls()) de la classe PackageManager. S'il renvoie false, alors vous pouvez lancer l'intent avec l'action ACTION_MANAGE_UNKNOWN_APP_SOURCES pour ouvrir la boîte de dialogue des paramètres où l'utilisateur peut autoriser l'application à installer des APK.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
&& !getPackageManager().canRequestPackageInstalls()) {
Intent unknownAppSourceIntent = new Intent()
.setAction(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES)
.setData(Uri.parse(String.format("package:%s", getPackageName())));
unknownAppSourceDialog.launch(unknownAppSourceIntent);
} else {
// L'application possède déjà l'autorisation d'installation, donc lancer l'installation de l'APK.
startActivity(intent);
}
Assurez-vous d'ajouter le code suivant à votre activité pour recevoir le résultat de l'intent.
ActivityResultLauncher unknownAppSourceDialog = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
if (result.getResultCode() == Activity.RESULT_OK) {
// L'utilisateur a autorisé l'application à installer des APK,
// nous pouvons donc maintenant lancer l'installation de l'APK.
startActivity(intent);
}
});
3 votes
Utilisez
ACTION_INSTALL_PACKAGE
. Cela a fonctionné pour moi à partir de l'édition de juin de la prévisualisation pour les développeurs N.2 votes
Aucune activité n'a été trouvée pour gérer l'intention { act=android.intent.action.INSTALL_PACKAGE ...
1 votes
Je n'ai aucun problème avec
ACTION_INSTALL_PACKAGE
, utilisant unFileProvider
pour servir le APK, lorsqu'il a été testé sur un Nexus 9 fonctionnant sous Android 7.0 (build NRD90M). Comparé à votreIntent
, en plus de la différence dans la chaîne d'action, je n'utilise pasFLAG_ACTIVITY_CLEAR_TOP
et j'utilisesetData()
plutôt quesetDataAndType()
.0 votes
D'où viens le "file" ? le dernier argument dans
getUriForFile
2 votes
Intent.ACTION_INSTALL_PACKAGE est obsolète, utilisez Intent.ACTION_VIEW
0 votes
Est-ce que ce comportement est toujours autorisé dans les applications avec la version
API 31
et supérieure? Ne serait-ce pas marqué par Play Protect comme un téléchargeur dynamique?