Hello guys, as per title in this post I ‘ll tell you how to download the app using a download manager and install it programmatically. For doing that will create a sample app, for more detail you can watch feature video as well. So let’s get started
Download and Install APK (Demo APP)
Create a new Project
Open Android Studio and create a new android project with any default template. Once project sync is finished, open app-level build.gradle. In this android example we’ll use Snackbar for that we have to add material io dependencies. so let’s do that
implementation 'com.google.android.material:material:1.0.0'
Add uses permission
I’m going to download the APK file so we need storage permission and Internet permission. Once the app is download successfully than we will install it programmatically. For doing that we must need REQUEST_INSTALL_PACKAGES permission. Let’ s all needed permission in android manifest.
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
Create a file provider
You guys, already aware that in android N or above we have to write a file provider. Let’s create a resource file inside res=>xml=> named is file_provider_paths.xml
<?xml version="1.0" encoding="utf-8"?> <paths> <external-path name="external" path="." /> <external-files-path name="external_files" path="." /> <files-path name="files" path="." /> </paths>
Now declare the file provider inside the manifest
<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/file_provider_paths" /> </provider>
Add some value inside strings.xml file
<resources> <string name="app_name">Download APK</string> <string name="downloading">Downloading...</string> <string name="title_file_download">APK is downloading</string> <string name="storage_access_required">Storage access is required to downloading the file.</string> <string name="storage_permission_denied">Storage permission request was denied.</string> <string name="ok">OK</string> </resources>
Create a download controller
Let’s create a file named is DownloadController, It’s controller is responsible to download file install it. This DownloadController we are using download manager for downloading APK.
package com.downloadapk.util import android.app.DownloadManager import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter import android.net.Uri import android.os.Build import android.os.Environment import android.widget.Toast import androidx.core.content.FileProvider import com.downloadapk.BuildConfig import com.downloadapk.R import java.io.File class DownloadController(private val context: Context, private val url: String) { companion object { private const val FILE_NAME = "SampleDownloadApp.apk" private const val FILE_BASE_PATH = "file://" private const val MIME_TYPE = "application/vnd.android.package-archive" private const val PROVIDER_PATH = ".provider" private const val APP_INSTALL_PATH = "\"application/vnd.android.package-archive\"" } fun enqueueDownload() { var destination = context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString() + "/" destination += FILE_NAME val uri = Uri.parse("$FILE_BASE_PATH$destination") val file = File(destination) if (file.exists()) file.delete() val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager val downloadUri = Uri.parse(url) val request = DownloadManager.Request(downloadUri) request.setMimeType(MIME_TYPE) request.setTitle(context.getString(R.string.title_file_download)) request.setDescription(context.getString(R.string.downloading)) // set destination request.setDestinationUri(uri) showInstallOption(destination, uri) // Enqueue a new download and same the referenceId downloadManager.enqueue(request) Toast.makeText(context, context.getString(R.string.downloading), Toast.LENGTH_LONG) .show() } private fun showInstallOption( destination: String, uri: Uri ) { // set BroadcastReceiver to install app when .apk is downloaded val onComplete = object : BroadcastReceiver() { override fun onReceive( context: Context, intent: Intent ) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { val contentUri = FileProvider.getUriForFile( context, BuildConfig.APPLICATION_ID + PROVIDER_PATH, File(destination) ) val install = Intent(Intent.ACTION_VIEW) install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) install.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) install.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true) install.data = contentUri context.startActivity(install) context.unregisterReceiver(this) // finish() } else { val install = Intent(Intent.ACTION_VIEW) install.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP install.setDataAndType( uri, APP_INSTALL_PATH ) context.startActivity(install) context.unregisterReceiver(this) // finish() } } } context.registerReceiver(onComplete, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) } }
Open the main activity layout file add one button
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/mainLayout" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".ui.MainActivity"> <Button android:id="@+id/buttonDownload" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@color/colorPrimaryDark" android:minHeight="40dp" android:text="Download" android:textAllCaps="false" android:textColor="@color/colorWhite" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
Create an extension file
For better coding practice, removing boilerplate code. We’ll write few extensions for AppCompatActivity and Snackbar. let’s create extension file and paste below code
package com.downloadapk.util import android.view.View import androidx.appcompat.app.AppCompatActivity import androidx.core.app.ActivityCompat import com.google.android.material.snackbar.Snackbar fun AppCompatActivity.checkSelfPermissionCompat(permission: String) = ActivityCompat.checkSelfPermission(this, permission) fun AppCompatActivity.shouldShowRequestPermissionRationaleCompat(permission: String) = ActivityCompat.shouldShowRequestPermissionRationale(this, permission) fun AppCompatActivity.requestPermissionsCompat( permissionsArray: Array<String>, requestCode: Int ) { ActivityCompat.requestPermissions(this, permissionsArray, requestCode) } fun View.showSnackbar(msgId: Int, length: Int) { showSnackbar(context.getString(msgId), length) } fun View.showSnackbar(msg: String, length: Int) { showSnackbar(msg, length, null, {}) } fun View.showSnackbar( msgId: Int, length: Int, actionMessageId: Int, action: (View) -> Unit ) { showSnackbar(context.getString(msgId), length, context.getString(actionMessageId), action) } fun View.showSnackbar( msg: String, length: Int, actionMessage: CharSequence?, action: (View) -> Unit ) { val snackbar = Snackbar.make(this, msg, length) if (actionMessage != null) { snackbar.setAction(actionMessage) { action(this) }.show() } }
Open main activity file
Let’s open the file and paste the below code. In this activity, we are checking storage permission first. If permission is granted than calling download function otherwise requesting for storage permission.
package com.downloadapk.ui import android.Manifest import android.content.pm.PackageManager import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import com.downloadapk.R import com.downloadapk.util.* import com.google.android.material.snackbar.Snackbar import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity() { companion object { const val PERMISSION_REQUEST_STORAGE = 0 } lateinit var downloadController: DownloadController override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // This apk is taking pagination sample app val apkUrl = "https://androidwave.com/source/apk/app-pagination-recyclerview.apk" downloadController = DownloadController(this, apkUrl) buttonDownload.setOnClickListener { // check storage permission granted if yes then start downloading file checkStoragePermission() } } override fun onRequestPermissionsResult( requestCode: Int, permissions: Array<out String>, grantResults: IntArray ) { if (requestCode == PERMISSION_REQUEST_STORAGE) { // Request for camera permission. if (grantResults.size == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // start downloading downloadController.enqueueDownload() } else { // Permission request was denied. mainLayout.showSnackbar(R.string.storage_permission_denied, Snackbar.LENGTH_SHORT) } } } private fun checkStoragePermission() { // Check if the storage permission has been granted if (checkSelfPermissionCompat(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED ) { // start downloading downloadController.enqueueDownload() } else { // Permission is missing and must be requested. requestStoragePermission() } } private fun requestStoragePermission() { if (shouldShowRequestPermissionRationaleCompat(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { mainLayout.showSnackbar( R.string.storage_access_required, Snackbar.LENGTH_INDEFINITE, R.string.ok ) { requestPermissionsCompat( arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), PERMISSION_REQUEST_STORAGE ) } } else { requestPermissionsCompat( arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), PERMISSION_REQUEST_STORAGE ) } } }
Conclusion
All done, In this tutorial, we have learned how to download APK and Install it programmatically. I hope it’s helpful for you, Help me by sharing this post with all your friends.
Keep in touch
If you have any queries, feel free to ask them in the comment section below. 🙂
27 Comments
didn’t work I received “there was problem parsing the package” when I press download
Hi,
I am a beginner in android studio please tell me in this project how to create a duplicate button with an action.
Is there a way to do that without enabling Unknown sources ?
Thank you
“There was a problem while parsing the package” after starting the activity. and even after adding android: usesCleartextTraffic = “true”
Greate!!
hi thanks a lot for sharing your code with us but can you give java code? i need java.tnx
I think a lot of people getting the “There was a problem while parsing the package” need to check that their “versionCode” in the build.gradle file is greater than the current package they have. Also make sure to “getPackageManager().canRequestPackageInstalls()” to make sure you have the permissions allowed to install unknown sources. I used this code on android 10 & 11 and it is working. Although I know kotlin enough to get around, I prefer java, so it would be good to see this converted to java as well.
if targetSdkVersion> 27 set in manifest
<application
….
android: usesCleartextTraffic = “true”
see: https://stackoverflow.com/questions/59818005/cannot-download-file-java-downloadmanager-api-28#comment105789844_59818005
ya no matter what APK i put in url get a There was a problem while parsing the package…
probably not made for later Android Devices like 10
can you give me code in java
can you give me code in java
How can I develop on Android 10?
can u give the code in java
i need java code too
I need also Java code
i need in java also, if you could provide it i will be apreciated
i need the java code too
will provide soon
Esta en java?
Sorry, Not available at this movement
I’m getting a message “There was a problem while parsing the package” after starting the activity.
Check you given permission correctly
Receving same error “There was a problem while parsing the package” even after giving permission
Same message for me “There was a problem while parsing the package”
Any solution ? (on emulator & device)
Your APK is not downloaded correctly
Hi,
Make sure you have given all the required permissions and check the file URI and see if you have the apk with the same name.