4

Starting an intent:

  Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
  CurrentFile = new File(getTempFileString());
  CurrentUri = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider", CurrentFile);
  intent.putExtra(MediaStore.EXTRA_OUTPUT, CurrentUri);
  intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
  startActivityForResult(intent, IMAGE_CAPTURE);

Used to work. It no longer does. My method that starts after the intent is:

public void onActivityResult(int requestCode, int resultCode, Intent data) {
}

The resultCode used to be RESULT_OK but is now RESULT_CANCELED.

If I stop checking the resultCode and just go past this, I find that the photo doesn't exist.

Based on CommonsWare's comment I extracted the logs. Of the 654 lines generated during this operation, four seem relevant.

2020-10-12 11:03:04.301 1471-1763/? E/MediaProvider: Creating a non-default top level directory or deleting an existing one is not allowed!
2020-10-12 11:03:04.310 477-2112/? I/AppsFilter: interaction: PackageSetting{240e1c6 com.[my app package]/10151} -> PackageSetting{193734c com.android.camera2/10124} BLOCKED
2020-10-12 11:03:04.553 390-9884/? W/ServiceManager: Permission failure: android.permission.SYSTEM_CAMERA from uid=10124 pid=11746
2020-10-12 11:03:14.097 11746-11746/? E/CAM_StateSavePic: exception while saving result to URI: Optional.of(content://[my app package].provider/external_files/[The SD card path I asked for]/1602518584301.jpg)

I am asking the file to get saved here:

new File(Environment.getExternalStorageDirectory(), getString(a.getApplicationInfo().labelRes))

This seems to be the issue.

Paul
  • 5,700
  • 5
  • 43
  • 67
  • 1
    Check Logcat to see if the camera app that happens to be your default is logging any messages of relevance around the time that you invoke this `Intent`. – CommonsWare Oct 12 '20 at 15:59
  • Thank you, that does lead somewhere. I updated the question and I have a little more to go on now. – Paul Oct 12 '20 at 16:16
  • 3
    You are not going to be able to write to the root of external storage next year, once your `targetSdkVersion` reaches 30. I *strongly* recommend that you write this image somewhere else -- your app should be able to contribute to `Pictures/`, for example. If you absolutely must write here in the short term, you will need to add `android:requestLegacyExternalStorage="true"` in the `` element of the manifest. – CommonsWare Oct 12 '20 at 16:24
  • Wow, I owe you a beer. I would have never found this. Thank you! – Paul Oct 12 '20 at 17:07

3 Answers3

12

Truly @CommonsWare deserves the credit here. The issue was the getTempFileString() call in the source code. I was attempting to save the file to a temp path so I could delete it right after. The file only exists shortly. Google blocked access to this. The Camera application received an error about not being able to save to this path, however it did not throw an error or display a message. It just returned a result of RESULT_CANCELED back to my application. I fixed this by updating the path used to the local cache directory getExternalCacheDir() and am using a sub directory inside that.

Edit: Adding the code that worked for me. Removed app specific text, and the error handling to keep it short and simple.

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

File path = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "My Application Directory");

if (!path.exists()) {
   path.mkdir();
}

File imageFile = File.createTempFile("Your file Name", ".jpg", path);

Context context = getActivity().getBaseContext();
Uri uri = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider", imageFile);

intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);

List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
   String packageName = resolveInfo.activityInfo.packageName;
   context.grantUriPermission(packageName, CurrentUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
}

startActivityForResult(intent, IMAGE_CAPTURE); // IMAGE_CAPTURE = 0
Paul
  • 5,700
  • 5
  • 43
  • 67
  • 3
    "I was attempting to save the file to a temp path so I could delete it right after" -- you might consider using `getCacheDir()`. `FileProvider` supports it and there are no permissions issues. Regardless, I'm glad that you got it working! – CommonsWare Oct 12 '20 at 17:21
  • Did you get getExternalCacheDir() to work? I get a resultcode of 0 back from the camera intent and when I look at logcat I see an access denied (Andriod 11 emulator) – Rob Feb 10 '21 at 17:50
  • 1
    @Rob I updated my answer with some source code – Paul Feb 10 '21 at 19:28
  • I had some problems so I went back to the Android docs and it now seems happy using getExternalFilesDir provided I do everything given in https://developer.android.com/training/camera/photobasics#java – Rob Feb 16 '21 at 11:28
1

you shouldn't use "getExternalStoragePublicDirectory()" because this is SHARED STORAGE behaviour and from (probably) 5 july 2021 all applications on the Play Store MUST target android SDK 30 and use Scoped Storage only (so you can't use "requestLegacyStorage", idk about "preserveLegacyStorage").

I suggest you to change the destination path to:

context.getExternalFilesDir(null).getPath();

Have a nice Day & a Nice Coding :D

Z3R0
  • 1,011
  • 10
  • 19
0

This is a strange behaviour but if you use MediaStore.EXTRA_OUTPUT flag method onActivityResult() will always return null data and you are expected to look for your data at location you pointed out here:

CurrentUri = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider", CurrentFile);

So there are two different ways of handling result you are looking for

Antares
  • 518
  • 6
  • 11