Android: Taking photo’s… It’s a tough world

Recently I’ve been working on an Android project for a client for which in the app we had left open the opportunity for the user to make or select a profile image that would be used in the application. Basic stuff you’dd say.

I just want to mention that is not a Samsung bashing article. I even don’t know if the problems described below are only for Samsung or more Android phone manufacturers have broken this simple peace of code…

The solution in fact was quite simple…
The code below will show you the definition of a FileProvider in the AndroidManifest.xml. The file_paths.xml file is just there to share that specific private folder through the FileProvider.
And then the magic happens in the activity. It’s quite simple. We create a File instance that points to the location where we’ll ask the camera to put the taken photo so that we can edit it afterwards. So then we just start the camera with the specific intent, and we start it for-result! Upon the onActivityResult(..) call we will get a RESULT_OK if a photo has been taken and we kept a reference to the temporary File instance so we now where the temporary photo is stored. Next we do some editing on the photo (make sure it’s turned in the right direction, it’s scaled well,…), we store it to it’s final location and we delete the temporary file.

At least so I thought…
Seems that this solution is working on the Samsung Galaxy S4. But off course not on the S3…
The I got then was `Permission Denial: opening provider android.support.v4.content.FileProvider from ProcessRecord`. It turn out that the camera app has no access on the File we are exposing (the temporary file). So what we have to do is to give all apps that might be chosen to react on our Intent access on the URI. So the final fix is to use `grantUriPermission(..)` to give the access to the default camera app (and potentially any other camera app the user has installed and thus can use to complete this action).

And then there was Samsung… (again, this is not a bashing story, but unfortunately there are a lot of Samsung devices around and we found the bug on older Samsung devices). So by looking to the code above I would say that this would work. And it does work on all my recent phones and all my old Nexus devices. On the Samsung Galaxy S7 it also worked. But not on the Samsung Galaxy S3 and S4 (European model, but I don’t think that matters). It started crashing in the Activity on line 78. The tempFile seems to be null. So after a little debugging I found out that on good phones after we called startActivityForResult(intent, requestCode) the camera was launched, and when we would come back from the camera the only method that was called was the onActivityResult(..). But for some reason on the Samsung devices the lifecycle had changed just a little bit… So after coming back from the camera the onCreate(savedInstanceState) is called, next onRestoreInstanceState(savedInstanceState) and only after that the onActivityResult(..) method.

So basically the fix is quite easy. After calling startActivityForResult(intent, requestCode) the onSaveInstanceState(outState) is called on which we just have to set the temporary file instance (that’s a Serialisable so we are lucky, the other option is to only store the tempProfileFile.getAbsolutePath() as a String). Then when returning from the camera, just before onActivityResult(..) is called the onRestoreInstanceState(savedInstanceState) is called in which we will assign tempProfileFile a value again. Only 8 lines of code and a headache later the issue is fixed :-) The full source of the Activity is below.

I’ve tested this on emulators with exactly the same Android version as my Samsung devices and they acted just fine with the first code. So this is not a backwards compatible thing, it really is a manufacturer with customised Android versions!