I am trying to implement a feature on an Android app that it can auto detect new app updates, then install and restart the app without any user actions required. The platform is Android 10 and the compile sdk version is 28,
The android device is used as the UI for an unattended vending machine, therefore I am trying to eliminate the user actions for updating the app.
I came up with the following code with the help from Android: install .apk programmatically [duplicate] but I encountered two issues:
- The check existence of file works but
file.delete();never worked. - The intent to run the apk file
startActivity(intent);can be started, but the OS pops up an error: There was a problem while parsing the package. I followed this post but it didn't work.
Below is the code:
- Code to check the updates:
new AppUpdaterUtils(this)
.setUpdateFrom(UpdateFrom.GITHUB)
.setGitHubUserAndRepo(user, repo)
.withListener(new AppUpdaterUtils.UpdateListener() {
@Override
public void onSuccess(Update update, Boolean isUpdateAvailable) {
if(isUpdateAvailable){
URL u = update.getUrlToDownload();
String url = "https://github.com/"+gitUser+"/"+repo+"/releases/download/v"+update.getLatestVersion()+"/"+appName;
update(url, appName);
}
}
@Override
public void onFailed(AppUpdaterError error) {
Log.e("UPDATE", "Failed");
}
})
.start();
- Code for download, install and restart:
public void update(String url, String fileName){
//get destination to update file and set Uri
String destination = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString();// + File.separator;
final Uri destUri = Uri.parse("file://" + destination +File.separator+ fileName);
Log.d("UPDATE", "destUri = " + destUri.toString());
//Delete update file if exists
File file = new File(destination + File.separator+fileName);
if (file.exists()){
boolean deleteSuccess = file.delete();
Log.d("UPDATE", "file exists! Delete = " + deleteSuccess);
}
//set downloadmanager
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
request.setDescription("add description here");
request.setTitle("update");
//set destination
request.setDestinationUri(destUri);
// get download service and enqueue file
final DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
final long downloadId = manager.enqueue(request);
file.setReadable(true, false);
//set BroadcastReceiver to install app when .apk is downloaded
BroadcastReceiver onComplete = new BroadcastReceiver() {
public void onReceive(Context ctxt, Intent intent) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Uri apkUri = FileProvider.getUriForFile(getApplicationContext(), BuildConfig.APPLICATION_ID + ".provider", file);
intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(apkUri,"application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
} else {
Uri apkUri = Uri.fromFile(file);
intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
startActivity(intent);
finish();
}
};
//register receiver for when .apk download is compete
registerReceiver(onComplete, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
}
- Added
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_download" path="Download"/>
</paths>
- Added provider to
AndroidManifest.xml
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
- Added permissions
WRITE_EXTERNAL_STORAGE,READ_EXTERNAL_STORAGE,INTERNETandREQUEST_INSTALL_PACKAGES