So far, there was an easy way to install an APK file, using this intent:
final Intent intent=new Intent(Intent.ACTION_VIEW)
I can just add a check for the version of Android OS, and use either methods, but as I've read, there should be a single method to use: FileProvider.
Well, as the saying goes, "it takes two to tango".
To use any particular scheme (file
, content
, http
, etc.), not only do you have to provide the data in that scheme, but the recipient needs to be able to support accepting the data in that scheme.
In the case of the package installer, support for content
as a scheme was only added in Android 7.0 (and then, perhaps only because I pointed out the problem).
Why does it occur?
Because Google (see this and this).
Is there anything wrong with the custom provider I've created?
Probably not.
Should I just add a check if it's Android API 24 and above, and if so, use the provider, and if not, use a normal Uri.fromFile call ?
Yes. Or, if you prefer, catch the ActivityNotFoundException
and react to that, or use PackageManager
and resolveActivity()
to see ahead of time if a given Intent
(e.g., one with a content
Uri
) will work properly.
If I use this, the support library actually loses its purpose, because it will be used for newer Android versions
The "support library" has little to do with newer-vs.-older Android versions. Only a small percentage of the classes across the various Android Support artifacts are backports or compatibility shims. Vast quantities of it — FileProvider
, ViewPager
, ConstraintLayout
, etc. — are simply classes that Google wanted to provide and support but wanted to make them available outside of the firmware.
Will the support library FileProvider be enough for all use cases
Only on Android 7.0+. Again, the stock Android package installer does not support content
schemes prior to Android 7.0.
just for those who wonder how to finally install an APK properly, here:
@JvmStatic
fun prepareAppInstallationIntent(context: Context, file: File, requestResult: Boolean): Intent? {
var intent: Intent? = null
try {
intent = Intent(Intent.ACTION_INSTALL_PACKAGE)//
.setDataAndType(
if (VERSION.SDK_INT >= VERSION_CODES.N)
androidx.core.content.FileProvider.getUriForFile(context, context.packageName + ".provider", file)
else
Uri.fromFile(file),
"application/vnd.android.package-archive")
.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true)
.putExtra(Intent.EXTRA_RETURN_RESULT, requestResult)
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
if (VERSION.SDK_INT < VERSION_CODES.JELLY_BEAN)
intent!!.putExtra(Intent.EXTRA_ALLOW_REPLACE, true)
} catch (e: Throwable) {
}
return intent
}
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/provider_paths"/>
</provider>
/res/xml/provider_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths>
<!--<external-path name="external_files" path="."/>-->
<external-path
name="files_root" path="Android/data/${applicationId}"/>
<external-path
name="external_storage_root" path="."/>
</paths>