I have an APK-File in /storage/emulated/0/Download/app-debug.apk.
I want to install this file by FileProvider because I am using Android N.
When I try to launch the intent logcat doesn't show any error but I receive following message on my phone:
There was a problem parsing the package.
What am I doing wrong?
MainActivity
public class MainActivity extends AppCompatActivity {
Button btnStartIntent;
String strRootPathInternalStorage = Environment.getExternalStorageDirectory().toString(); //-> /storage/emulated/0 //-> /storage/emulated/0/Download/
String strApkToInstall = "app-debug.apk";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/*
--> Booooh bad way! <--
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());
*/
btnStartIntent = (Button)findViewById(R.id.btnStartIntent);
btnStartIntent.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
installApk(MainActivity.this, strRootPathInternalStorage+"/Download/"+strApkToInstall);
}
});
}
public static void installApk(Context context, String apkPath) {
if (context == null || TextUtils.isEmpty(apkPath)) {
return;
}
File file = new File(apkPath);
Intent intent = new Intent(Intent.ACTION_VIEW);
if (Build.VERSION.SDK_INT >= 24) {
//provider authorities
Uri apkUri = FileProvider.getUriForFile(context, "com.spicysoftware.test.provider", file);
//Granting Temporary Permissions to a URI
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
} else {
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
}
context.startActivity(intent);
}
}
Manifest
<?xml version="1.0" encoding="utf-8"?>
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.spicysoftware.test.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths"/>
</provider>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
provider_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="Download" path="Download" />
</paths>
For anyone who has the same problem.
I had to specify the exactly folder with getExternalFilesDir();
MainActivity
public class MainActivity extends AppCompatActivity {
Button btnStartIntentFileProvider;
String strRootPathInternalStorage = Environment.getExternalStorageDirectory().toString();
String strApkToInstall = "app-debug.apk";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnStartIntentFileProvider = (Button)findViewById(R.id.btnFileProvider);
btnStartIntentFileProvider.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
File fileApkToInstall = new File(getExternalFilesDir("Download"), strApkToInstall);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Uri apkUri = FileProvider.getUriForFile(MainActivity.this, BuildConfig.APPLICATION_ID + ".provider", fileApkToInstall);
Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.setData(apkUri);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
MainActivity.this.startActivity(intent);
} else {
Uri apkUri = Uri.fromFile(fileApkToInstall);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
MainActivity.this.startActivity(intent);
}
}
});
}
}
Manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.spicysoftware.test">
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.spicysoftware.test.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths"/>
</provider>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
provider_paths (in XML Folder)
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-files-path name="Download" path="Download/" />
</paths>
After doing research and trying everything out, Here is my working code for android sdk 31:
1- created FileProvider and put it res/xml and named it "provider_paths":
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external" path="." />
</paths>
2- 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"/>
3-In the main activity I have a function to download the apk from a server and receive the file and launch an installation intent to install the new version:
void checkUpdate()
{
try{
//check if file exists delete it
File file=new File("/sdcard/Download/app-debug.apk");
if(file.exists())
{
file.delete();
Log.d(TAG,"Update file exists, Deleting it!");
}
int versionCode = BuildConfig.VERSION_CODE;
String url="https://Website.com/index.php?version="+versionCode;
Log.d(TAG,url);
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(20, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
.build();
Request request = new Request.Builder()
.url(url)
.build();
Response response = null;
response = client.newCall(request).execute();
if(response.code()!=200)
{
runOnUiThread(() -> Toast.makeText(getApplicationContext(),"No update found",Toast.LENGTH_LONG).show());
return;
}
//downloading the file
InputStream is = response.body().byteStream();
BufferedInputStream input = new BufferedInputStream(is);
String strRootPathInternalStorage = Environment.getExternalStorageDirectory().toString();
String path = strRootPathInternalStorage+"/"+Environment.DIRECTORY_DOWNLOADS+"/app-debug.apk";
Log.d(TAG,path);
ResponseBody body = response.body();
long contentLength = body.contentLength();
Log.d(TAG,""+contentLength);
BufferedSource source = body.source();
BufferedSink sink = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
sink = Okio.buffer(Okio.sink(Paths.get(path)));
}else
{
Log.e(TAG,"Check this");
}
Buffer sinkBuffer = sink.buffer();
long totalBytesRead = 0;
int bufferSize = 8 * 1024;
for (long bytesRead; (bytesRead = source.read(sinkBuffer, bufferSize)) != -1; )
{
sink.emit();
totalBytesRead += bytesRead;
}
sink.flush();
sink.close();
source.close();
//finished downloading the file start installing
runOnUiThread(() -> {
try {
File fileApkToInstall = new File("/sdcard/Download/app-debug.apk");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Uri apkUri = FileProvider.getUriForFile(MainActivity.this, BuildConfig.APPLICATION_ID + ".provider", fileApkToInstall);
Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.setData(apkUri);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
MainActivity.this.startActivity(intent);
} else {
Uri apkUri = Uri.fromFile(fileApkToInstall);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
MainActivity.this.startActivity(intent);
}
} catch (Exception e) {
e.printStackTrace();
}
});
}catch (Exception e)
{
e.printStackTrace();
}
}
4- the website update logic is to get the current version from the app and check in the server to see if it's the last version if yes than return 404 or something similar and if no than return the apk file.
Related
I thoroughly checked that pdf file is
in
/storage/emulated/0/Download/Abcd.pdf"
but can't open it with intent.
I opened it in various viewers and some oh them result in a error: "can't open file". Microsoft word says: check file in the device, but Abcd.pdf file is opened well when I opened it in file directory in file system of android.
Did I set the route wrong?
AndroidManifest.xml
<provider
android:authorities="${applicationId}.provider"
android:name="android.support.v4.content.FileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths"/>
</provider>
MainActivity.Java
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent=new Intent();
File mypath=new File( Environment.getExternalStorageDirectory().getAbsolutePath() + "/Download/","Abcd.pdf");
Log.e("download",mypath.getAbsolutePath());
//this log says : /storage/emulated/0/Download/Abcd.pdf
Uri pdfUri = FileProvider.getUriForFile(getApplicationContext(), getApplicationContext().getPackageName() + ".provider", mypath);
Log.e("download",mypath.exists()+"");
if (mypath.exists()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Log.e("download","result : "+pdfUri.getPath());
intent = new Intent(Intent.ACTION_VIEW);
intent.setType("application/pdf");
intent.putExtra(MediaStore.EXTRA_OUTPUT, pdfUri);
}else{
}
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
try {
startActivity(intent);
}
catch (ActivityNotFoundException e) {
Log.e("error","error"+e);
}
}
}
res/xml/provider_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="storage/emulated/0"
path="."/>
</paths>
I am using this function :
public void openPdf(LocalFile magazine) {
if (BuildConfig.DEBUG)
Log.d(TAG, "openPdf() called with: magazine = [" + magazine + "]");
Intent intent = new Intent(Intent.ACTION_VIEW);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
File file = new File(Uri.parse(magazine.getPath()).getPath());
Uri uri = FileProvider.getUriForFile(getContext(),
getContext().getApplicationContext().getPackageName() + ".provider", file);
intent.setDataAndType(uri, "application/pdf");
intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
} else {
intent.setDataAndType(Uri.parse(magazine.getPath()), "application/pdf");
}
try {
startActivity(intent);
} catch (Throwable t) {
t.printStackTrace();
//attemptInstallViewer();
}
}
This is works fine for me.
I've one question by which this problem might be resolved, Have you wrote Storage Permission in Manifest?
Also is your App has Permission to Read External Storage? Check this from Mobile Settings and it would start working automatically.
Here is how you can initialize Permissions Statement in Manifest File.
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
If it's given in Manifest and also your Mobile has guaranteed the Permissions and App is not working then share the full code of Activity, I might help you in that.
I have a project I'm working on that requires a file to be downloaded normally, and then opened (generally in a PDF reader).
I have a broadcast receiver class which looks like this
public class DownloadBroadcastReceiver extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
String action = intent.getAction();
if (action.equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
{
DownloadManager.Query query = new DownloadManager.Query();
query.setFilterById(intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0));
DownloadManager manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
Cursor cursor = manager.query(query);
if (cursor.moveToFirst()) {
if (cursor.getCount() > 0) {
int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
Long download_id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID,0);
String downloadFilePath = null;
String downloadFileLocalUri = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
File file = new File(Uri.parse(downloadFileLocalUri).getPath());
// status contain Download Status
// download_id contain current download reference id
if (status == DownloadManager.STATUS_SUCCESSFUL) {
String fname=cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_TITLE));
File pdfFile = new File(Environment.getExternalStorageDirectory()+"/Downloads/"+fname);//File path
if (pdfFile.isFile()) //Checking if the file exists or not
{
Uri path = Uri.fromFile(pdfFile);
Intent objIntent = new Intent(Intent.ACTION_VIEW);
objIntent.setDataAndType(path, "application/pdf");
objIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
context.startActivity(objIntent);//Starting the pdf viewer
} else {
Log.d("OO",Environment.getExternalStorageDirectory()+"/Downloads/"+fname);
Log.d("OO",fname);
// Toast.makeText(getApplicationContext(),"Test",Toast.LENGTH_LONG).show();
}
}
}
}
cursor.close();
}
}
}
The Manifest looks like this
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="uk.org.bridgewaterha.boardapp">
<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.ACCESS_DOWNLOAD_MANAGER" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".pdf"></activity>
<receiver android:name=".DownloadBroadcastReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.DOWNLOAD_COMPLETE"/>
</intent-filter>
</receiver>
<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>
</application>
</manifest>
the paths file is as below
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="Download" path="."/>
<cache-path name="cache" path="/" />
</paths>
The problem I'm having is that in the Broadcast receiver class, the line
if (pdfFile.isFile())
ALWAYS returns false.
I've tried a few variations I've come across on the web, but none seem to be working at all.
Any tips greatly appreciated.
After downloading you have to check your file is mFile.exists() or not...
use this to open pdf file:
I think your application default behavior is not opening it directly.
so we need to use chooser
Intent myIntent = new Intent(Intent.ACTION_VIEW);
myIntent.setData(Uri.fromFile(file));
Intent j = Intent.createChooser(myIntent, "Choose an application to open with:");
startActivity(j);
while (cursor.moveToNext()) {
if (cursor.getCount() > 0) {
Log.e("filelistingloop","loopfollowingss "+cursor.getCount());
int status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
if (status == DownloadManager.STATUS_SUCCESSFUL) {
String file = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
if (file != null) {
File mFile = new File(Uri.parse(file).getPath());
final String fileName = mFile.getAbsolutePath().substring(mFile.getAbsolutePath().lastIndexOf('/') + 1, mFile.getAbsolutePath().length());
if (mFile.exists()){
Toast.makeText(yourActivit.this," File donwloaded successfully",Toast.LENGTH_LONG).show();
//here you can open your pdf
}
}
// So something here on success
}else if (status == DownloadManager.STATUS_FAILED){
int message = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_REASON));
// So something here on failed.
Toast.makeText(getActivity(),"File Not donwloaded "+ message,Toast.LENGTH_LONG).show();
}
}
}
I'm trying to implement auto download and install apk in my application using file provider. For android version less than Oreo it works fine but getting problem while running app on device with android 8.0 and above. It works fine till Nougat, but doesn't works with Oreo. Downloading apk from server working fine but fails to install new apk.
Here is my code.
File file, folder;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
Utility.checkPermissionCamera(Main2Activity.this);
Utility.checkPermissionInstall(Main2Activity.this);
String fileName = "AutoDownloadApplication.apk";
folder = new File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString());
Logger.e("FOLDER", " " + folder);
file = new File(folder.getAbsolutePath(), fileName);
Log.e("File ", "" + file);
final Uri uri = FileProvider.getUriForFile(Main2Activity.this, BuildConfig.APPLICATION_ID + ".provider", file);
Logger.e("Check URI ", "" + uri);
if (Utility.checkPermissionCamera(Main2Activity.this)) {
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(StaticConstant.DOWNLOADAPK));
request.setDescription("Downloading New Apk");
request.setTitle(Main2Activity.this.getString(R.string.app_name));
//set destination
request.setDestinationInExternalFilesDir(Main2Activity.this, BuildConfig.APPLICATION_ID + ".provider", fileName);
// get download service and enqueue file
final DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
final long downloadId = manager.enqueue(request);
//set BroadcastReceiver to install app when .apk is downloaded
BroadcastReceiver onComplete = new BroadcastReceiver() {
public void onReceive(Context ctxt, Intent intent) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.setData(uri);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(intent);
} else {
Intent install = new Intent(Intent.ACTION_VIEW);
install.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
install.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
install.setDataAndType(uri,
manager.getMimeTypeForDownloadedFile(downloadId));
startActivity(install);
}
unregisterReceiver(this);
}
};
//register receiver for when .apk download is compete
registerReceiver(onComplete, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
}
}
Manifest file:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="MyPackageName">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<permission
android:name="android.permission.REQUEST_INSTALL_PACKAGES"
android:protectionLevel="normal" />
<uses-permission
android:name="android.permission.REQUEST_DELETE_PACKAGES"
tools:ignore="ProtectedPermissions" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="MyPackageName.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths" />
</provider>
</application>
Here is the file_paths xml:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="Download" path="Android/data/MyPackageName/files/Download"/>
</paths>
Please add the <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/> permission in Manifest.xml first.
We should check the result of getPackageManager().canRequestPackageInstalls() if the SDK version is equal or large than 26.
The code is below:
private void checkIsAndroidO() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
boolean result = getPackageManager().canRequestPackageInstalls();
if (result) {
installApk();
} else {
// request the permission
ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.REQUEST_INSTALL_PACKAGES}, INSTALL_PACKAGES_REQUESTCODE);
}
} else {
installApk();
}
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case INSTALL_PACKAGES_REQUESTCODE:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
installApk();
} else {
Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
startActivityForResult(intent, GET_UNKNOWN_APP_SOURCES);
}
break;
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case GET_UNKNOWN_APP_SOURCES:
checkIsAndroidO();
break;
default:
break;
}
}
I hope it will help you.
You can read more about FileProvider implementation in details how it works from below article:
https://inthecheesefactory.com/blog/how-to-share-access-to-file-with-fileprovider-on-android-nougat/en
The soluciton of JohnWatsonDev Worked perfectly!
Just one thing, to call your app directly, is just put:
intent.setData(Uri.parse("package:" + getPackageName()));
Before call
startActivityForResult(intent, GET_UNKNOWN_APP_SOURCES);
My question is how to share multiple files with ShareCompat intentBuilder in android.
My code throws exception:
android.content.ActivityNotFoundException: No Activity found to handle
Intent { act=android.intent.action.SEND_MULTIPLE flg=0x80001
pkg=com.google.android.gm (has clip) (has extras) }
My code is:
static void with(Activity activity, ArrayList<String> imageFile,String app){
ArrayList<Uri> imagesUri=new ArrayList<>();
for (String i:imageFile)
imagesUri.add(FileProvider.getUriForFile(
activity, FILES_AUTHORITY, new File(i)));
Intent shareIntent = ShareCompat.IntentBuilder.from(activity).getIntent();
shareIntent.setAction(Intent.ACTION_SEND_MULTIPLE);
shareIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM,imagesUri);
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
activity.startActivity(shareIntent);
}
Is throws error for every app (package name).
the following works for me:
ShareCompat.IntentBuilder shareIntent = ShareCompat.IntentBuilder.from(activity).setType("image/*");
for(String address : addresses ) {
File file = new File(address);
Uri imageUri = FileProvider.getUriForFile(
activity,
"app.signature.fileprovider",
file);
shareIntent.addStream(imageUri);
}
Intent intent=shareIntent.getIntent();
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(Intent.createChooser(intent, "Choose Application"));
you should use addStream() to add multiple files.
After a very hard time searching on sites like stackoverflow. I found a solution in #Ian Lake's blog post that works for me and makes sense too.
ShareCompat.IntentBuilder shareIntent = ShareCompat.IntentBuilder.from((Activity) context)
.setType("video/image/*");
String[] files = parentDirectory.list();
if (files != null) {
for (String address : files) {
File file = new File(address);
File filePath = new File(context.getExternalFilesDir(null), "vids"); //vids is a directory local to my app in i.e storage/0/emulated/Android/data/com.myapp.videoeditor/vids
File newFile = new File(filePath, file.getName());
if (address.contains("cut_video")) {
Uri uri = FileProvider.getUriForFile(
context,
"com.myapp.videoeditor.fileprovider",
newFile);
shareIntent.addStream(uri);
}
}
}
Intent intent = shareIntent.getIntent();
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
context.startActivity(Intent.createChooser(intent, "Choose..."));
res/xml/file_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-files-path name="external_files" path="vids/"/>
</paths>
Manifest
<application
....
....>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.myapp.videoeditor.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<!-- ressource file to create -->
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths">
</meta-data>
...
...
</application>
Did you add intent filter in AndroidManifest.xml for the activity that is supposed to receive the files?
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
I'm developing an Android application and I have to open some files.
This is my code using intent:
public class FacturaActivity extends Activity {
(...)
public void downloadInvoice(View view) {
File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() +"/"+ filename);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file),"application/pdf");
intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
startActivity(intent);
}
}
File is in the root directory of the SD card and I can manually open it.
Problem
Application is closed when it arrives at startActivity(intent). I think the problem is in AndroidManifest.xml file, but I don't know how to put it correctly.
AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="8" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme"
android:name="###.MyApplication" > <!--cant show complete name-->
<activity
android:name="###.MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".FacturaActivity" >
</activity>
</application>
LogCat
07-03 15:49:13.094: E/AndroidRuntime(1032): FATAL EXCEPTION: main
07-03 15:49:13.094: E/AndroidRuntime(1032): java.lang.IllegalStateException: Could not execute method of the activity
(...)
07-03 15:49:13.094: E/AndroidRuntime(1032): Caused by: android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.VIEW dat=file:///mnt/sdcard/201209_F2012212782.PDF typ=application/pdf flg=0x40000000 }
07-03 15:49:13.094: E/AndroidRuntime(1032): at android.app.Instrumentation.checkStartActivityResult(Instrumentation.java:1408)
07-03 15:49:13.094: E/AndroidRuntime(1032): at android.app.Instrumentation.execStartActivity(Instrumentation.java:1378)
07-03 15:49:13.094: E/AndroidRuntime(1032): at android.app.Activity.startActivityForResult(Activity.java:2817)
07-03 15:49:13.094: E/AndroidRuntime(1032): at android.app.Activity.startActivity(Activity.java:2923)
Can you help me to complete AndroidManifest? Or how can I open that pdf?
The problem is that there is no app installed to handle opening the PDF. You should use the Intent Chooser, like so:
File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() +"/"+ filename);
Intent target = new Intent(Intent.ACTION_VIEW);
target.setDataAndType(Uri.fromFile(file),"application/pdf");
target.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
Intent intent = Intent.createChooser(target, "Open File");
try {
startActivity(intent);
} catch (ActivityNotFoundException e) {
// Instruct the user to install a PDF reader here, or something
}
As of API 24, sending a file:// URI to another app will throw a FileUriExposedException. Instead, use FileProvider to send a content:// URI:
public File getFile(Context context, String fileName) {
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
return null;
}
File storageDir = context.getExternalFilesDir(null);
return new File(storageDir, fileName);
}
public Uri getFileUri(Context context, String fileName) {
File file = getFile(context, fileName);
return FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".provider", file);
}
You must also define the FileProvider in your manifest:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.mydomain.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths" />
</provider>
Example file_paths.xml:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-files-path name="name" path="path" />
</paths>
Replace "name" and "path" as appropriate.
To give the PDF viewer access to the file, you also have to add the FLAG_GRANT_READ_URI_PERMISSION flag to the intent:
private void displayPdf(String fileName) {
Uri uri = getFileUri(this, fileName);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, "application/pdf");
// FLAG_GRANT_READ_URI_PERMISSION is needed on API 24+ so the activity opening the file can read it
intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_GRANT_READ_URI_PERMISSION);
if (intent.resolveActivity(getPackageManager()) == null) {
// Show an error
} else {
startActivity(intent);
}
}
See the FileProvider documentation for more details.
String dir="/Attendancesystem";
public void displaypdf() {
File file = null;
file = new File(Environment.getExternalStorageDirectory()+dir+ "/sample.pdf");
Toast.makeText(getApplicationContext(), file.toString() , Toast.LENGTH_LONG).show();
if(file.exists()) {
Intent target = new Intent(Intent.ACTION_VIEW);
target.setDataAndType(Uri.fromFile(file), "application/pdf");
target.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
Intent intent = Intent.createChooser(target, "Open File");
try {
startActivity(intent);
} catch (ActivityNotFoundException e) {
// Instruct the user to install a PDF reader here, or something
}
}
else
Toast.makeText(getApplicationContext(), "File path is incorrect." , Toast.LENGTH_LONG).show();
}
Kotlin version below (Updated version of #paul-burke response:
fun openPDFDocument(context: Context, filename: String) {
//Create PDF Intent
val pdfFile = File(Environment.getExternalStorageDirectory().absolutePath + "/" + filename)
val pdfIntent = Intent(Intent.ACTION_VIEW)
pdfIntent.setDataAndType(Uri.fromFile(pdfFile), "application/pdf")
pdfIntent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)
//Create Viewer Intent
val viewerIntent = Intent.createChooser(pdfIntent, "Open PDF")
context.startActivity(viewerIntent)
}
The reason you don't have permissions to open file is because you didn't grant other apps to open or view the file on your intent. To grant other apps to open the downloaded file, include the flag(as shown below): FLAG_GRANT_READ_URI_PERMISSION
Intent browserIntent = new Intent(Intent.ACTION_VIEW);
browserIntent.setDataAndType(getUriFromFile(localFile), "application/pdf");
browserIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION|
Intent.FLAG_ACTIVITY_NO_HISTORY);
startActivity(browserIntent);
And for function:
getUriFromFile(localFile)
private Uri getUriFromFile(File file){
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
return Uri.fromFile(file);
}else {
return FileProvider.getUriForFile(itemView.getContext(), itemView.getContext().getApplicationContext().getPackageName() + ".provider", file);
}
}
Want to chime in with the answers above. The code is nearly identical, except it's in an Android Jetpack Compose composable (and therefore in Kotlin). That, and I did two videos talking through it.
Here's the happy path version, (clocking in at 10 minutes).
For the whole hog, this behemoth 30 minute screenshow has me provide a significant amount of context and a/b options of the code.
If you want to see the code, you can find it in this repo branch.