Search code examples
androidkotlinandroid-intentfragmentapk

Install apk from fragment


I am trying to install an app from my fragment. I want the unknown source permission message to be displayed and then the installation process to take place. The problem is that on my first installation, app seems to be crashed. Of course, next time (when installing another apk) this problem disappears. I did the following steps:

In my viewModel :

    fun installApp(uri: Uri) {
        viewModelScope.launch(context = exceptionHandler + DEFAULT) {
            val promptInstall = Intent(Intent.ACTION_VIEW, uri)
            promptInstall.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true)
            promptInstall.setDataAndType(uri, "application/vnd.android" + ".package-archive")
            promptInstall.flags = Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK
            promptInstall.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
            startActivityMutableLiveData.postValue(promptInstall.toOneTimeEvent())
        }
    }

and then in my fragment :

viewModel.startActivityLiveData.observe(viewLifecycleOwner, Observer { oneTimeEvent ->
                (oneTimeEvent.getValue() as Intent).startActivityFromIntent(requireActivity())})

And finally this is my extension function :

fun Intent.startActivityFromIntent(context: Context) = (context as Activity).startActivity(this)


Solution

  • Well, it seems that the process I mentioned above has a problem(it crashed!) in Android 10 and above. Here is the solution I found:

    private fun Uri.installApp(context: Context) {
     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            if (!context.packageManager.canRequestPackageInstalls()) {
                startForResult.launch(Intent(ACTION_MANAGE_UNKNOWN_APP_SOURCES))
                  Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES).setData(
            } else {
                viewModel.installApp(this)
            }
        } else {
            viewModel.installApp(this)
        }
    

    and we must do as below :

        private val startForResult =
        registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
            when (result.resultCode) {
                RESULT_OK -> {
                    // your logic...
                }
                RESULT_CANCELED -> {
                    //do something
                }
                else -> {
                }
            }
        }