Android & Kotlin

Download and Install APK Programmatically

Pinterest LinkedIn Tumblr

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. 🙂

28 Comments

  1. I just got this code working on Android 11. Zubair Rehman Dar is correct that the filename must match your app name, so in my case I had to change the FILE_NAME constant to com.example.downloadapk.apk in the DownloadController.

  2. didn’t work I received “there was problem parsing the package” when I press download

  3. Hi,
    I am a beginner in android studio please tell me in this project how to create a duplicate button with an action.

  4. Is there a way to do that without enabling Unknown sources ?
    Thank you

  5. “There was a problem while parsing the package” after starting the activity. and even after adding android: usesCleartextTraffic = “true”

  6. hi thanks a lot for sharing your code with us but can you give java code? i need java.tnx

  7. 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.

  8. 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

  9. I’m getting a message “There was a problem while parsing the package” after starting the activity.

    • Prathamesh

      Receving same error “There was a problem while parsing the package” even after giving permission

    • goulkreek

      Same message for me “There was a problem while parsing the package”

    • Zubair Rehman Dar

      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.

Write A Comment