android


Android - Share (startActivity) Does Not Work On 7.0 [duplicate]


The app crashes when trying to open a file. It work below Android N, but on Android N it crashes. It only crashes when I try to open a file from the SD card, not from the system partition. Some permission problem?
Sample code:
File file = new File("/storage/emulated/0/test.txt");
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), "text/*");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent); // Crashes on this line
Log:
android.os.FileUriExposedException: file:///storage/emulated/0/test.txt exposed beyond app through Intent.getData()
Edit:
When targeting Android N, file:// URIs are not allowed anymore. We should use content:// URIs instead. However, my app needs to open files in root directories. Any ideas?
If your targetSdkVersion is 24 or higher, we have to use FileProvider class to give access to the particular file or folder to make them accessible for other apps.
Steps to replace file:// uri with content:// uri:
add a FileProvider tag in AndroidManifest.xml under tag.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
...
<application
...
<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>
then create a provider_paths.xml file in xml folder under res folder. Folder may be needed to create if it doesn't exist.
The content of the file is shown below. It describes that we would like to share access to the External Storage at root folder (path=".") with the name external_files.
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="."/>
</paths>
The final step is to change the line of code below in
Uri photoURI = Uri.fromFile(createImageFile());
to
Uri photoURI = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider", createImageFile());
Hope this will help...:)
Please refer, full code and solution has been explained here.
Besides the solution using the FileProvider, there is another way to work around this. Simply put
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());
in Application.onCreate(). In this way the VM ignores the file URI exposure.
Method
builder.detectFileUriExposure()
enables the file exposure check, which is also the default behavior if we don't setup a VmPolicy.
I encountered a problem that if I use a content:// URI to send something, some apps just can't understand it. And downgrading the target SDK version is not allowed. In this case my solution is useful.
If your targetSdkVersion is 24 or higher, you can not use file: Uri values in Intents on Android 7.0+ devices.
Your choices are:
Drop your targetSdkVersion to 23 or lower, or
Put your content on internal storage, then use FileProvider to make it available selectively to other apps
For example:
Intent i=new Intent(Intent.ACTION_VIEW, FileProvider.getUriForFile(this, AUTHORITY, f));
i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(i);
(from this sample project)
First you need to add a provider to your AndroidManifest
<application
...>
<activity>
....
</activity>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.your.package.fileProvider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths" />
</provider>
</application>
now create a file in xml resource folder (if using android studio you can hit Alt + Enter after highlighting file_paths and select create a xml resource option)
Next in the file_paths file enter
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path path="Android/data/com.your.package/" name="files_root" />
<external-path path="." name="external_storage_root" />
</paths>
This example is for external-path you can refere here for more options.
This will allow you to share files which are in that folder and its sub-folder.
Now all that's left is to create the intent as follows:
MimeTypeMap mime = MimeTypeMap.getSingleton();
String ext = newFile.getName().substring(newFile.getName().lastIndexOf(".") + 1);
String type = mime.getMimeTypeFromExtension(ext);
try {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri contentUri = FileProvider.getUriForFile(getContext(), "com.your.package.fileProvider", newFile);
intent.setDataAndType(contentUri, type);
} else {
intent.setDataAndType(Uri.fromFile(newFile), type);
}
startActivityForResult(intent, ACTIVITY_VIEW_ATTACHMENT);
} catch (ActivityNotFoundException anfe) {
Toast.makeText(getContext(), "No activity found to open this attachment.", Toast.LENGTH_LONG).show();
}
EDIT: I added the root folder of the sd card in the file_paths. I have tested this code and it does work.
If your app targets API 24+, and you still want/need to use file:// intents, you can use hacky way to disable the runtime check:
if(Build.VERSION.SDK_INT>=24){
try{
Method m = StrictMode.class.getMethod("disableDeathOnFileUriExposure");
m.invoke(null);
}catch(Exception e){
e.printStackTrace();
}
}
Method StrictMode.disableDeathOnFileUriExposure is hidden and documented as:
/**
* Used by lame internal apps that haven't done the hard work to get
* themselves off file:// Uris yet.
*/
Problem is that my app is not lame, but rather doesn't want to be crippled by using content:// intents which are not understood by many apps out there. For example, opening mp3 file with content:// scheme offers much fewer apps than when opening same over file:// scheme. I don't want to pay for Google's design faults by limiting my app's functionality.
Google wants developers to use content scheme, but the system is not prepared for this, for years apps were made to use Files not "content", files can be edited and saved back, while files served over content scheme can't be (can they?).
#palash k answer is correct and worked for internal storage files, but in my case I want to open files from external storage also, my app crashed when open file from external storage like sdcard and usb, but I manage to solve the issue by modifying provider_paths.xml from the accepted answer
change the provider_paths.xml like below
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path path="Android/data/${applicationId}/" name="files_root" />
<root-path
name="root"
path="/" />
</paths>
and in java class(No change as the accepted answer just a small edit)
Uri uri=FileProvider.getUriForFile(getActivity(), BuildConfig.APPLICATION_ID+".provider", File)
This help me to fix the crash for files from external storages, Hope this will help some one having same issue as mine
:)
I used Palash's answer given above but it was somewhat incomplete, I had to provide permission like this
Intent intent = new Intent(Intent.ACTION_VIEW);
Uri uri;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
uri = FileProvider.getUriForFile(this, getPackageName() + ".provider", new File(path));
List<ResolveInfo> resInfoList = getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
String packageName = resolveInfo.activityInfo.packageName;
grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
}else {
uri = Uri.fromFile(new File(path));
}
intent.setDataAndType(uri, "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
Using the fileProvider is the way to go.
But you can use this simple workaround:
WARNING: It will be fixed in next Android release -
https://issuetracker.google.com/issues/37122890#comment4
replace:
startActivity(intent);
by
startActivity(Intent.createChooser(intent, "Your title"));

Related Links

How to get Artist ,Album in Media Player For API LEVEL 2.2
How to implement the Pause action for the android VideoView?
what should be used to store comments given by any users such as one in any blog or in FB?.
To use the JNI, or not to use the JNI (Android performance)
OpenGL ES API error (no context)
h.264 and mpeg header
NullPointerException When Switching between views
google maps not showing properly and force close
ROR/AndroidRuntime: Caused by: android.database.sqlite.SQLiteException: no such column type : [closed]
unable to get lat/lng values from JSON output
Qt GUI is not behaving normally on Android
Is there a limit on how many different apps can single developer publish on Android Market?
android fragment missing from View Hierarchy
List view with image
Implementing the Model-View-Controller pattern
Color the typed letters in android

Categories

HOME
dns
pda
components
sitecore
edge
cakephp-3.4
ado.net
vsm
computer-science
openpyxl
java.util.logging
ipmitool
jsonpath
shared-libraries
flash-player
cumulocity
http-post
capistrano3
easyphp
google-picker
nim
replication
simpleitk
poedit
qliksense
highstock
quantlib
user-defined-functions
python-2.6
utorrent
cart
glib
erb
batching
python-pptx
escpos
smoothstate.js
apache-directory
mesosphere
xbmc
lampp
convex-optimization
x-ray
avx
er-diagram
lsyncd
template-toolkit
change-tracking
vcloud-director-rest-api
microstation
adafruit
resourcemanager
watch-os-2
wso2developerstudio
i386
clipboard.js
jtwig
android-wake-lock
angular-ui-typeahead
gcloud-ruby
boost-serialization
fink
thread-sleep
kendo-upload
computer-architecture
cgi-bin
eoferror
typemock
keyup
git-ftp
matlab-deployment
zend-currency
infosphere-spl
ejb-2.x
jbake
viewer
genymotion-call
radiant
koala
ecslidingviewcontroller
hibernate-entitymanager
kendo-dataviz
scrypt
joomla-template
actiondispatch
cascadingdropdown
idn
android-loadermanager
panda3d
boost-msm
jquery-address
multiplatform
unattended-processing
drupal-fivestar
web-garden
todos
dsoframer

Resources

Mobile Apps Dev
Database Users
javascript
java
csharp
php
android
MS Developer
developer works
python
ios
c
html
jquery
RDBMS discuss
Cloud Virtualization
Database Dev&Adm
javascript
java
csharp
php
python
android
jquery
ruby
ios
html
Mobile App
Mobile App
Mobile App