I have an Android application written in Java that works with photos and PDF files. I have the following permissions requested:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
Some are older and some are for the new API levels. I am targeting API 33. The app is able to accept shared/sent files of image type or PDF type:
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/pdf" />
</intent-filter>
The application can interact with photos or PDF in the following ways:
User takes a photo using the camera but instantiated by my app. I save this photo to a proper location that I can see and work with. No problems here.
User selects to open an existing photo from their gallery. The popup photo selection tool can show the user photos from their gallery be it photos they took or even photos sitting in their Download directory. Again, no problems here.
User selects a photo or PDF file while browsing the files on their device. Here is where the problem lies.
The Send action starts my app and passes in the file information in the bundle. The code here is simplified with variable names shortened and null checks removed for simplicity.
FragmentActivity PhotosActivityHolder = getActivity();
Intent i = PhotosActivityHolder.getIntent();
Bundle b = i.getExtras();
Object o = b.get(Intent.EXTRA_STREAM);
Uri u = (Uri) o;
// At this point the URI looks something like:
// content://com.google.android.apps.photos.contentprovider/-1/1/content%3A%2F%2Fmedia%2Fexternal%2Fimages%2Fmedia%2F1000000034/REQUIRE_ORIGINAL/NONE/image%2Fjpeg/1813409384**strong text**
At this point I attempt to load the file. For non-local files I use a ContentResolver. This can happen when the user browses to a photo which is backed up in Google Photos but is no longer local on the device. In the cases of the Send/Share intent, these files are typically never backed up and are just local. Because the files are local I attemp to read them as such. Again I have stripped some try-catch blocks and IF NULL checks:
ContentResolver cr = PhotosActivityHolder.getContentResolver();
String[] projection = { MediaStore.MediaColumns.DATA };
Cursor cur = cr.query(u, projection, null, null, null);
cur.moveToFirst();
String fileName = cur.getString(0);
cur.close();
// At this point, fileName is something like:
// /storage/emulated/0/Pictures/IMG_20230330_165859.jpg
Next I ensure the file exists and read it into a stream. Here is where the crash happens:
File file = new File(fileName);
if (file.exists()) {
FileInputStream fis
try {
fis = new FileInputStream(file);
}
catch (FileNotFoundException e) {
// Here's where I end up. The file exists, however I get a FileNotFoundException.
// The exception mentions Access Denied internally
}
}
What am I doing wrong? This worked fine on older targeted API levels. I need to be able to read files not in my app's allocated space due to this Share/Send intent. I have read online that Download is especially tricky to get to, but I can't get anything at all. I have all the newly required permissions enabled.