Je souhaite savoir s'il est possible d'installer par programme une apk téléchargée dynamiquement à partir d'une application Android personnalisée.
Je souhaite savoir s'il est possible d'installer par programme une apk téléchargée dynamiquement à partir d'une application Android personnalisée.
Réponses:
Vous pouvez facilement lancer un lien Play Store ou une invite d'installation:
Intent promptInstall = new Intent(Intent.ACTION_VIEW)
.setDataAndType(Uri.parse("content:///path/to/your.apk"),
"application/vnd.android.package-archive");
startActivity(promptInstall);
ou
Intent goToMarket = new Intent(Intent.ACTION_VIEW)
.setData(Uri.parse("https://play.google.com/store/apps/details?id=com.package.name"));
startActivity(goToMarket);
Cependant, vous ne pouvez pas installer .apks sans l' autorisation explicite de l'utilisateur ; sauf si l'appareil et votre programme sont enracinés.
/sdcard
, car c'est faux sur Android 2.2+ et d'autres appareils. Utilisez Environment.getExternalStorageDirectory()
plutôt.
File file = new File(dir, "App.apk");
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
startActivity(intent);
J'ai eu le même problème et après plusieurs tentatives, cela a fonctionné pour moi de cette façon. Je ne sais pas pourquoi, mais la définition des données et du type séparément a foiré mon intention.
setData()
entraînerait la suppression du paramètre type. Vous DEVEZ utiliser setDataAndType()
si vous voulez donner des valeurs pour les deux. Ici: developer.android.com/reference/android/content/…
Les solutions apportées à cette question s'appliquent toutes aux targetSdkVersion
articles 23 et inférieurs. Pour Android N, c'est-à-dire API niveau 24 et supérieur, cependant, ils ne fonctionnent pas et se bloquent avec l'exception suivante:
android.os.FileUriExposedException: file:///storage/emulated/0/... exposed beyond app through Intent.getData()
Cela est dû au fait qu'à partir d'Android 24, l' Uri
adressage du fichier téléchargé a changé. Par exemple, un fichier d'installation nommé appName.apk
stocké sur le système de fichiers externe principal de l'application avec le nom du package com.example.test
serait comme
file:///storage/emulated/0/Android/data/com.example.test/files/appName.apk
pour API 23
et en dessous, alors que quelque chose comme
content://com.example.test.authorityStr/pathName/Android/data/com.example.test/files/appName.apk
pour API 24
et au-dessus.
Plus de détails à ce sujet peuvent être trouvés ici et je ne vais pas les parcourir.
Pour répondre à la question targetSdkVersion
de24
et au-dessus, il faut suivre ces étapes: Ajoutez ce qui suit au fichier AndroidManifest.xml:
<application
android:allowBackup="true"
android:label="@string/app_name">
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.authorityStr"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/paths"/>
</provider>
</application>
2. Ajoutez le paths.xml
fichier suivant au xml
dossier surres
dans src, main:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="pathName"
path="pathValue"/>
</paths>
le pathName
celui illustré dans l'exemple de contenu uri ci-dessus et pathValue
c'est le chemin réel sur le système. Ce serait une bonne idée de mettre un "." (sans guillemets) pour pathValue ci-dessus si vous ne souhaitez pas ajouter de sous-répertoire supplémentaire.
Écrivez le code suivant pour installer l'apk avec le nom appName.apk
sur le système de fichiers externe principal:
File directory = context.getExternalFilesDir(null);
File file = new File(directory, fileName);
Uri fileUri = Uri.fromFile(file);
if (Build.VERSION.SDK_INT >= 24) {
fileUri = FileProvider.getUriForFile(context, context.getPackageName(),
file);
}
Intent intent = new Intent(Intent.ACTION_VIEW, fileUri);
intent.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true);
intent.setDataAndType(fileUri, "application/vnd.android" + ".package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
context.startActivity(intent);
activity.finish();
Aucune autorisation n'est également nécessaire lors de l'écriture dans le répertoire privé de votre propre application sur le système de fichiers externe.
J'ai écrit ici une bibliothèque AutoUpdate dans laquelle j'ai utilisé ce qui précède.
.authorityStr
après context.getPackageName()
cela devrait fonctionner.
Eh bien, j'ai creusé plus profondément et trouvé des sources d'application PackageInstaller à partir d'Android Source.
https://github.com/android/platform_packages_apps_packageinstaller
D'après le manifeste, j'ai trouvé qu'il fallait une autorisation:
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
Et le processus réel d'installation se produit après confirmation
Intent newIntent = new Intent();
newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mPkgInfo.applicationInfo);
newIntent.setData(mPackageURI);
newIntent.setClass(this, InstallAppProgress.class);
String installerPackageName = getIntent().getStringExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME);
if (installerPackageName != null) {
newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, installerPackageName);
}
startActivity(newIntent);
Je veux juste partager le fait que mon fichier apk a été enregistré dans le répertoire "Data" de mon application et que je devais changer les autorisations sur le fichier apk pour qu'il soit lisible par tout le monde afin de permettre son installation de cette façon, sinon le système lançait "Erreur d'analyse: il y a un problème d'analyse du package"; donc en utilisant la solution de @Horaceman qui fait:
File file = new File(dir, "App.apk");
file.setReadable(true, false);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
startActivity(intent);
Cela peut aider beaucoup les autres!
Première:
private static final String APP_DIR = Environment.getExternalStorageDirectory().getAbsolutePath() + "/MyAppFolderInStorage/";
private void install() {
File file = new File(APP_DIR + fileName);
if (file.exists()) {
Intent intent = new Intent(Intent.ACTION_VIEW);
String type = "application/vnd.android.package-archive";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Uri downloadedApk = FileProvider.getUriForFile(getContext(), "ir.greencode", file);
intent.setDataAndType(downloadedApk, type);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
} else {
intent.setDataAndType(Uri.fromFile(file), type);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
getContext().startActivity(intent);
} else {
Toast.makeText(getContext(), "ّFile not found!", Toast.LENGTH_SHORT).show();
}
}
Deuxièmement: Pour Android 7 et supérieur, vous devez définir un fournisseur dans le manifeste comme ci-dessous!
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="ir.greencode"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/paths" />
</provider>
Troisièmement: Définissez path.xml dans le dossier res / xml comme ci-dessous! J'utilise ce chemin pour le stockage interne si vous voulez le changer pour autre chose, il y a quelques façons! Vous pouvez accéder à ce lien: FileProvider
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="your_folder_name" path="MyAppFolderInStorage/"/>
</paths>
Quatrième: vous devez ajouter cette autorisation dans le manifeste:
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
Permet à une application de demander l'installation de packages. Les applications ciblant des API supérieures à 25 doivent détenir cette autorisation pour utiliser Intent.ACTION_INSTALL_PACKAGE.
Veuillez vous assurer que les autorités du fournisseur sont les mêmes!
Oui c'est possible. Mais pour cela, vous avez besoin du téléphone pour installer des sources non vérifiées. Par exemple, slideMe fait cela. Je pense que la meilleure chose que vous puissiez faire est de vérifier si l'application est présente et d'envoyer une intention pour l'Android Market. vous devez utiliser quelque chose que le schéma d'url pour Android Market.
market://details?id=package.name
Je ne sais pas exactement comment démarrer l'activité mais si vous démarrez une activité avec ce type d'URL. Il devrait ouvrir le marché Android et vous donner le choix d'installer les applications.
Il convient de noter que si vous utilisez le DownloadManager
pour lancer votre téléchargement, assurez-vous de l'enregistrer dans un emplacement externe, par exemple setDestinationInExternalFilesDir(c, null, "<your name here>).apk";
. L'intention d'un type d'archive de package ne semble pas aimer le content:
schéma utilisé pour les téléchargements vers un emplacement interne, mais il aime file:
. (Essayer d'encapsuler le chemin d'accès interne dans un objet File, puis d'obtenir le chemin d'accès ne fonctionne pas non plus, même s'il en résulte unfile:
URL, car l'application ne analysera pas l'apk; il semble que ce doit être externe.)
Exemple:
int uriIndex = cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI);
String downloadedPackageUriString = cursor.getString(uriIndex);
File mFile = new File(Uri.parse(downloadedPackageUriString).getPath());
Intent promptInstall = new Intent(Intent.ACTION_VIEW)
.setDataAndType(Uri.fromFile(mFile), "application/vnd.android.package-archive")
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
appContext.startActivity(promptInstall);
N'oubliez pas de demander des autorisations:
android.Manifest.permission.WRITE_EXTERNAL_STORAGE
android.Manifest.permission.READ_EXTERNAL_STORAGE
Ajoutez dans AndroidManifest.xml le fournisseur et l'autorisation:
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
...
<application>
...
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
</application>
Créer le fournisseur de fichiers XML res / xml / provider_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="external"
path="." />
<external-files-path
name="external_files"
path="." />
<cache-path
name="cache"
path="." />
<external-cache-path
name="external_cache"
path="." />
<files-path
name="files"
path="." />
</paths>
Utilisez l'exemple de code ci-dessous:
public class InstallManagerApk extends AppCompatActivity {
static final String NAME_APK_FILE = "some.apk";
public static final int REQUEST_INSTALL = 0;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// required permission:
// android.Manifest.permission.WRITE_EXTERNAL_STORAGE
// android.Manifest.permission.READ_EXTERNAL_STORAGE
installApk();
}
...
/**
* Install APK File
*/
private void installApk() {
try {
File filePath = Environment.getExternalStorageDirectory();// path to file apk
File file = new File(filePath, LoadManagerApkFile.NAME_APK_FILE);
Uri uri = getApkUri( file.getPath() ); // get Uri for each SDK Android
Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.setData( uri );
intent.setFlags( Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_ACTIVITY_NEW_TASK );
intent.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true);
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
intent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, getApplicationInfo().packageName);
if ( getPackageManager().queryIntentActivities(intent, 0 ) != null ) {// checked on start Activity
startActivityForResult(intent, REQUEST_INSTALL);
} else {
throw new Exception("don`t start Activity.");
}
} catch ( Exception e ) {
Log.i(TAG + ":InstallApk", "Failed installl APK file", e);
Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG)
.show();
}
}
/**
* Returns a Uri pointing to the APK to install.
*/
private Uri getApkUri(String path) {
// Before N, a MODE_WORLD_READABLE file could be passed via the ACTION_INSTALL_PACKAGE
// Intent. Since N, MODE_WORLD_READABLE files are forbidden, and a FileProvider is
// recommended.
boolean useFileProvider = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
String tempFilename = "tmp.apk";
byte[] buffer = new byte[16384];
int fileMode = useFileProvider ? Context.MODE_PRIVATE : Context.MODE_WORLD_READABLE;
try (InputStream is = new FileInputStream(new File(path));
FileOutputStream fout = openFileOutput(tempFilename, fileMode)) {
int n;
while ((n = is.read(buffer)) >= 0) {
fout.write(buffer, 0, n);
}
} catch (IOException e) {
Log.i(TAG + ":getApkUri", "Failed to write temporary APK file", e);
}
if (useFileProvider) {
File toInstall = new File(this.getFilesDir(), tempFilename);
return FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID, toInstall);
} else {
return Uri.fromFile(getFileStreamPath(tempFilename));
}
}
/**
* Listener event on installation APK file
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == REQUEST_INSTALL) {
if (resultCode == Activity.RESULT_OK) {
Toast.makeText(this,"Install succeeded!", Toast.LENGTH_SHORT).show();
} else if (resultCode == Activity.RESULT_CANCELED) {
Toast.makeText(this,"Install canceled!", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this,"Install Failed!", Toast.LENGTH_SHORT).show();
}
}
}
...
}
essaye ça
String filePath = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME));
String title = filePath.substring( filePath.lastIndexOf('/')+1, filePath.length() );
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File(filePath)), "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // without this flag android returned a intent error!
MainActivity.this.startActivity(intent);
Ajoutez d'abord la ligne suivante à AndroidManifest.xml:
<uses-permission android:name="android.permission.INSTALL_PACKAGES"
tools:ignore="ProtectedPermissions" />
Utilisez ensuite le code suivant pour installer apk:
File sdCard = Environment.getExternalStorageDirectory();
String fileStr = sdCard.getAbsolutePath() + "/MyApp";// + "app-release.apk";
File file = new File(fileStr, "TaghvimShamsi.apk");
Intent promptInstall = new Intent(Intent.ACTION_VIEW).setDataAndType(Uri.fromFile(file),
"application/vnd.android.package-archive");
startActivity(promptInstall);
UpdateNode fournit une API pour Android pour installer des packages APK à partir d'une autre application.
Vous pouvez simplement définir votre mise à jour en ligne et intégrer l'API dans votre application - c'est tout.
Actuellement, l'API est en état bêta, mais vous pouvez déjà faire quelques tests vous-même.
À côté de cela, UpdateNode propose également d'afficher des messages via le système - assez utile si vous voulez dire quelque chose d'important à vos utilisateurs.
Je fais partie de l'équipe de développement client et j'utilise au moins la fonctionnalité de message pour ma propre application Android.
Essayez ceci - Écrivez sur le manifeste:
uses-permission android:name="android.permission.INSTALL_PACKAGES"
tools:ignore="ProtectedPermissions"
Écrivez le code:
File sdCard = Environment.getExternalStorageDirectory();
String fileStr = sdCard.getAbsolutePath() + "/Download";// + "app-release.apk";
File file = new File(fileStr, "app-release.apk");
Intent promptInstall = new Intent(Intent.ACTION_VIEW).setDataAndType(Uri.fromFile(file),
"application/vnd.android.package-archive");
startActivity(promptInstall);
Dans Android Oreo et les versions supérieures, nous devons aborder différentes méthodes pour installer apk par programme.
private void installApkProgramatically() {
try {
File path = activity.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS);
File file = new File(path, filename);
Uri uri;
if (file.exists()) {
Intent unKnownSourceIntent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES).setData(Uri.parse(String.format("package:%s", activity.getPackageName())));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (!activity.getPackageManager().canRequestPackageInstalls()) {
startActivityForResult(unKnownSourceIntent, Constant.UNKNOWN_RESOURCE_INTENT_REQUEST_CODE);
} else {
Uri fileUri = FileProvider.getUriForFile(activity.getBaseContext(), activity.getApplicationContext().getPackageName() + ".provider", file);
Intent intent = new Intent(Intent.ACTION_VIEW, fileUri);
intent.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true);
intent.setDataAndType(fileUri, "application/vnd.android" + ".package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(intent);
alertDialog.dismiss();
}
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Intent intent1 = new Intent(Intent.ACTION_INSTALL_PACKAGE);
uri = FileProvider.getUriForFile(activity.getApplicationContext(), BuildConfig.APPLICATION_ID + ".provider", file);
activity.grantUriPermission("com.abcd.xyz", uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
activity.grantUriPermission("com.abcd.xyz", uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
intent1.setDataAndType(uri,
"application/*");
intent1.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent1.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent1.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
startActivity(intent1);
} else {
Intent intent = new Intent(Intent.ACTION_VIEW);
uri = Uri.fromFile(file);
intent.setDataAndType(uri,
"application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
} else {
Log.i(TAG, " file " + file.getPath() + " does not exist");
}
} catch (Exception e) {
Log.i(TAG, "" + e.getMessage());
}
}
Dans Oreo et les versions supérieures, nous avons besoin d'une autorisation d'installation de ressources inconnue. donc dans le résultat de l'activité, vous devez vérifier le résultat pour la permission
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case Constant.UNKNOWN_RESOURCE_INTENT_REQUEST_CODE:
switch (resultCode) {
case Activity.RESULT_OK:
installApkProgramatically();
break;
case Activity.RESULT_CANCELED:
//unknown resouce installation cancelled
break;
}
break;
}
}