6

I'm making an APK Install app and I've been through a lot. Especially, versioning. to do that, i looked up some posting and StackOverFlow. but All the postings make me more confused now. So, What is the right way?

first of all, please check my code below about installing for each version. and Let's talk about what's the right way.

KitKat(API 19), Lollipop(API 21), MashMellow(API 23)

fun installApkBelowNougat(apkFile: File) {
  val apkUri = Uri.fromFile(apkFile)
  val intent = Intent(Intent.ACTION_VIEW).apply {
    setDataAndType(apkUri, "application/vnd.android.package-archive")
    flags = Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK
  }
}

in this case, all solutions are same and we have to use Uri.fromFile(file) to get a Uri.

Nougat(API 24)

fun installApkInNougat(apkFile: File) {
  val apkUri = FileProvider.getUriForFile(applicationContext, applicationContext.packageName + ".fileProvider", apkFile)
  val intent = Intent(Intent.ACTION_VIEW).apply {
    setDataAndType(apkUri, "application/vnd.android.package-archive")
    flags = Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK
    addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
  }
}

in this case, we can't use Uri.fromFile(file) anymore. So instead of it, We have to use FileProvider since Nougat. and We also have to write <proivder/> in AndroidManifest.xml like this.

...
<provider
  android:name="androidx.core.content.FileProvider"
  android:authorities="${applicationId}.fileProvider"
  android:exported="false"
  android:grantUriPermissions="true">
     <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/filepath" />
     </provider>
...

Someone say we don't need to use setDataAndType(). instead of it they just use setData() in Android7(Nougat). which one is right way? are both of those fine? and we need to set a Flag Intent.FLAG_GRANT_READ_URI_PERMISSION.
Edit: You can find when you use setDataAndType() or just one of both here.

enter image description here

Oreo(API 26), Pie(API 28), Q(API 29), R(API 30)

fun installApkMoreThanO(apkFile: File) {
  f (!this.packageManager.canRequestPackageInstalls()) {
            val unknownIntent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES).setData(
                Uri.parse(String.format("package:%s", this.packageName))
            )
            resultLauncher.launch(unknownIntent)
        } else {
            val apkUri = FileProvider.getUriForFile(applicationContext, applicationContext.packageName + ".fileProvider", apkFile)
            val intent = Intent(Intent.ACTION_VIEW, apkUri).apply {
                putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true)
                setDataAndType(apkUri, "application/vnd.android.package-archive")
                flags = Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK
                addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
            }
            startActivity(intent)
            finish()
        }
}

in this case, from Oreo, We have to add this permission <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> to install APK file. if don't, the app will just crash without any sign. and we have to use Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES because The way in which permission is granted has changed since Android8(Oreo). before Oreo we can grant permission for all apps at once. but since Oreo, we have to grant permission for each apps individually.

and we can use registerActivitResult for the result of permission. it means If you have never allowed permission of the app so far, you must allow permission, and after you allow it, the system returns Activity.RESULT_OK, or Activity.RESULT_CANCELED. and then you can do what you want.

S(API 31)

fun installApkMoreThanR(apkFile: File) {
 val apkUri = FileProvider.getUriForFile(applicationContext, applicationContext.packageName + ".fileProvider", apkFile)
 val intent = Intent(Intent.ACTION_VIEW, apkUri).apply {
     putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, false)
     setDataAndType(apkUri, "application/vnd.android.package-archive")
     flags = Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK
     addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
   }
}

in this case, it looks that i go back to a little bit old way but it really works and it's fine. and it makes the same result as the Android 8 ~ 10's way. the reason why i don't use Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES) is that we can only receive RESULT_CANCELED even if we grant a permission of the app. it always return Activity.RESULT_CANCELED in Android12. i don't know why. and some people say we have to use PackageInstaller API because ACTION_VIEW and ACTION_INSTALL_PACKAGE are deprecated in Android 10. but i tested it and ACTION_VIEW is not deprecated. But which way is better?? we have to use PackageInstaller API since Android 10?

CodingBruceLee
  • 657
  • 1
  • 5
  • 19

0 Answers0