Install apk FileProvider - android

I need to download .apk from server and promt user to install it. Its an actual update of b2b app and not malware.
For new android versions i need to use FileProvider.
Download works fine with download manager and on download is also triggered.
File is visible in device file explorer and File apk has correct path.
Allowed it in unknown apps.
Full app path: file:///storage/emulated/0/Android/data/com.example.trebovanje/files/Download/update_1.0-5.apk
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="."/>
</paths>
<provider
android:name=".GenericFileProvider"
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>
And the code for install intent:
File apk = new File(Uri.parse(file).getPath());
if(apk.exists()) {
MimeTypeMap map = MimeTypeMap.getSingleton();
String ext = MimeTypeMap.getFileExtensionFromUrl(apk.getName());
String mimeType = map.getMimeTypeFromExtension(ext);
if (mimeType == null) mimeType = "*/*";
Intent promptInstall = new Intent(Intent.ACTION_VIEW);
promptInstall.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
promptInstall.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true);
promptInstall.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
promptInstall.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { //new androids
try {
Uri data = getFileUri(getApplicationContext(), apk);
promptInstall.setDataAndType(data, mimeType);
startActivity(promptInstall);
}catch(Exception e){
Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG).show();
}
}else{ //old anrdoids
Uri data = Uri.fromFile(apk);
promptInstall.setDataAndType(data, mimeType);
startActivity(promptInstall);
}
}
private Uri getFileUri(Context context, File file) {
return FileProvider.getUriForFile(context, getPackageName() + ".provider", file);
}
I get always get "there is a problem parsing the package".

Related

FileProvider is unable to attach file to email client on Android 11

I used to attach a logs file, that I was storing in externalCacheDir, to a mail client.
Using this : intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(logFile))
However, starting from Android 11 it just stopped working. So I tried using FileProvider. But once the mail client opens it says "Couldn't attach file".
I have tried doing everything I could find here on SO and on the internet in general. But I can't seem to find the solution that will actually work.
Activity :
val logFile: File = File(globalContext.externalCacheDir, "MyLogFile.log")
fun Activity.openMail(
type: String = "text/plain"
): Boolean {
val intent = Intent(Intent.ACTION_SENDTO)
intent.putExtra(Intent.EXTRA_EMAIL, arrayOf("<some address...>"))
intent.putExtra(Intent.EXTRA_SUBJECT, "Android Logs")
intent.putExtra(Intent.EXTRA_TEXT, "some text")
val uri = FileProvider.getUriForFile(this, "${BuildConfig.APPLICATION_ID}.fileprovider", logFile)
intent.putExtra(Intent.EXTRA_STREAM, uri)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
intent.type = type
intent.data = Uri.parse("mailto:")
if (intent.resolveActivity(packageManager) != null) {
startActivity(Intent.createChooser(intent, "Send email via::"))
return true
} else {
return false
}
}
Manifest :
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<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/provider_paths" />
</provider>
provider_paths :
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-cache-path name="external_cache" path="."/>
</paths>

How does FileProvider choose/differentiate provider paths in provider_path.xml?

I got to use FileProvider in order to install an apk. Everything works well now but I don't really understand the behaviour FileProvider follows in order to choose the correct path defined inside the provider_paths.xml. Here's my code:
Manifest.xml
<provider
android:name="android.support.v4.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>
main_activity.java
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
intent = new Intent(Intent.ACTION_VIEW);
File apk = new File("/sdcard/example.apk");
String fileType = "application/vnd.android.package-archive";
intent.setDataAndType(Uri.fromFile(apk), fileType);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
} else {
intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
File directory = new File(Environment.getExternalStorageDirectory().toString());
File apk = new File(directory, "example.apk");
Uri fileUri = FileProvider.getUriForFile(this, valoresGenerales.appContext.getPackageName() + ".provider", apk);
String type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(MimeTypeMap.getFileExtensionFromUrl(apk.getName()));
intent.setDataAndType(fileUri, type);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
startActivity(intent);
provider_paths.xml
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="." />
<files-path name="files_path" path="Download"/>
</paths>
What does make FileProvider choose between external_path or files_path (or whatever defined in the file) depends on?

Opening a downloaded PDF file

I'm trying to open downloaded pdf file trough implicit intent using FileProvider.
I'm using DownloadManager for downloading pdf file from remote server, It's working fine. Which is store at it's destination.
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(DownloadURL));
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI | DownloadManager.Request.NETWORK_MOBILE);
request.setAllowedOverRoaming(false);
request.setTitle(mFilename);
request.setDescription("Downloading...");
request.setVisibleInDownloadsUi(true);
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "/FOLDER_NAME/" + mFilename);
After Finishing Download i want to open it.
public void OpenPdfFile(){
File sharedFile = new File(Environment.DIRECTORY_DOWNLOADS, "/FOLDER_NAME/" + mFilename);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri uri = FileProvider.getUriForFile(mContext, BuildConfig.APPLICATION_ID+ ".provider", sharedFile);
intent.setDataAndType(uri, "application/pdf");
PackageManager pm = mContext.getPackageManager();
if (intent.resolveActivity(pm) != null) {
mContext.startActivity(intent);
}
}
in Manifest file
<provider
android:name="android.support.v4.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>
and the provider_paths.xml as like
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external-path" path="." />
</paths>
it troughs me this error
java.lang.IllegalArgumentException: Failed to find configured root that contains /Download/FOLDER_NAME/demo_presentationfile.PDF
Any suggestions ?
File sharedFile = new File(Environment.DIRECTORY_DOWNLOADS, "/FOLDER_NAME/" + mFilename);
That evaluates to an impossible file system path. Which you would see if you inspected the value of sharedFile.getAbsolutePath().
Change to:
File sharedFile = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOWNLOADS), "FOLDER_NAME/" + mFilename);

Intent to install APK file - Android Nougat

I searched extensively and did not find the problem. When you try to install an APK file using an Intent in Android Nougat, it simply does not install and displays the following warning: "There was a problem parsing the package".
It works perfectly to open PDF files, for example, with settings to open this type of file (.PDF). However to install .apk files does not work.
LogCat does not show any errors and I can not reach any solution.
What could be wrong?
The following code:
Manifest:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="br.com.xxxxxx.xxxxxx.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/filepaths"/>
</provider>
xml/filepaths:
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="storage/emulated/0" path="."/>
Activity:
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
try {
Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
File file = new File(getContext().getFilesDir(), "app-debug.apk");
Uri uri = FileProvider.getUriForFile(getContext(), BuildConfig.APPLICATION_ID + ".fileprovider", file);
intent.setDataAndType(uri, "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(intent);
finish();
} catch (Exception e) {
e.printStackTrace();
}
}else{
try {
Intent intent = new Intent(Intent.ACTION_VIEW);
File file = new File(Environment.getExternalStorageDirectory() + "/app-debug.apk");
intent.setAction(android.content.Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(intent);
finish();
} catch (Exception e) {
e.getMessage();
}
}
Please, what could be wrong with this code? Can someone help me?
I had to change the intent for N (and higher) and remove the type designation. Once I did that the install worked as expected.
So for N:
Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
File file = new File(getContext().getFilesDir(), "app-debug.apk");
Uri uri = FileProvider.getUriForFile(getContext(), BuildConfig.APPLICATION_ID + ".fileprovider", file);
intent.setData(uri)
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(intent);
finish();

install apk from url

I am trying to install an APK from a URL. This is my code:
Intent promptInstall = new Intent(android.content.Intent.ACTION_VIEW);
promptInstall.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
promptInstall.setDataAndType(Uri.parse("http://10.0.2.2:8081/MyAPPStore/apk/Teflouki.apk"), "application/vnd.android.package-archive" );
startActivity(promptInstall);
But I have this problem:
05-10 15:09:29.511: ERROR/AndroidRuntime(1668): android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.VIEW dat=http://10.0.2.2:8081/MyAPPStore/apk/Teflouki.apk typ=application/vnd.android.package-archive flg=0x10000000 }
Thanks in advance.
You should download xxx.apk in storage before install by:
Intent promptInstall = new Intent(android.content.Intent.ACTION_VIEW);
promptInstall.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
promptInstall.setDataAndType(Uri.parse("storage/xxx.apk"), "application/vnd.android.package-archive" );
startActivity(promptInstall);
This won't help if the app is not available on the mearketplace, but in case it is:
Uri marketUri = Uri.parse("market://search?q=pname:com.appmaker.tefloukipackage");
Intent marketIntent = new Intent(Intent.ACTION_VIEW, marketUri);
try {
context.startActivity(marketIntent);
} catch (ActivityNotFoundException ex) {
showAlertDialog(context, "Error", "Could not launch the market application.", true, null);
}
Follow along.
In your module manifest, add
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
//Inside application block
<application>
<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_path"/>
</provider>
</application>
In your module res/xml folder, if not create this folder, with file provider_path.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="."/>
</paths>
And use this method.
private fun updateApplication(activity: Activity) {
//This will get you the root directory path
val externalStoragePublicDirectory: String =
Environment.getExternalStorageDirectory().path
val externalStoragePublicDirectoryFile =
File(externalStoragePublicDirectory, "MyApp" + ".apk")
val uri = FileProvider.getUriForFile(
activity.applicationContext,
activity.applicationContext.packageName + ".provider",
externalStoragePublicDirectoryFile
)
val installAppIntent = Intent(Intent.ACTION_VIEW)
.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
.setDataAndType(
uri,
"application/vnd.android.package-archive"
)
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true)
activity.startActivity(installAppIntent)
//This will close your app, remove if not needed
exitProcess(0)
}
Important, also go to your phone settings, search for unknown sources, enable it in old devices, but in new devices, search for your app and allow it the permission to install new app packages. Only then you will get the pop up to install the app.

Categories

Resources