Reputation: 3315
I'm using Activity Result API (with FileProvider) to save image from Camera to app cache folder and then pass it URI to another activity.
It works if activity orientation does not change during operation.
But when user rotate Camera activity (launched by ActivityResultContracts.TakePicture contract) - image will not be saved (file created with 0 bytes). And I'm getting error while decoding image in activity where I'm pass URI:
android.graphics.ImageDecoder$DecodeException: Failed to create image decoder with message 'unimplemented'Input contained an error.
I tried to solve problem in obvious way - programmatically lock orientation before camera callback launch and unlock after, but this only works the first time (may sound strange, but that's how it is), when orientation is changed again - problem persists. It looks like after changing orientation connection between current activity result callback and the Camera activity is broken. After research, it turned out that if I do NOT launch activity to open image - image is correctly saved. I tried to launch activity with delay and in another thread - but without success.
I still can't find reason why image doesn't save when orientation changed.
Main activity:
class MainActivity : AppCompatActivity() {
private val tmpImageUri by lazy { FileUtils.getTmpFileUri(applicationContext) }
private val takeImageResult = registerForActivityResult(ActivityResultContracts.TakePicture()) { isSuccess ->
if (isSuccess) {
// We have successfully (actually no) saved the image and can work with it via tmpImageUri
launchEditActivity(tmpImageUri)
}
}
private fun takePictureFromCamera() {
takeImageResult.launch(tmpImageUri)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding.button.setOnClickListener {
takePictureFromCamera()
}
}
private fun launchEditActivity(uri: Uri){
val intent = Intent(this, EditActivity::class.java).apply {
data = uri
}
startActivity(intent)
}
}
Activity where I'm pass URI:
class EditActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
loadImage()
}
private fun loadImage(){
intent.data?.let { uri ->
model.viewModelScope.launch(Dispatchers.IO) {
val bitmap = FileUtils.loadBitmapFromUri(applicationContext, uri)
runOnUiThread {
model.setImage(bitmap)
}
}
}
}
}
File utils:
fun getTmpFileUri(context: Context): Uri {
clearCache(context)
val tmpFile = File.createTempFile("tmp", ".jpg", context.cacheDir)
return FileProvider.getUriForFile(context, "${BuildConfig.APPLICATION_ID}.provider", tmpFile)
}
private fun clearCache(context: Context){
context.cacheDir.deleteRecursively()
}
fun loadBitmapFromUri(context: Context, uri: Uri): Bitmap{
val bitmap: Bitmap = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
val src = ImageDecoder.createSource(context.contentResolver, uri)
ImageDecoder.decodeBitmap(src) { decoder, info, source ->
decoder.isMutableRequired = true
}
} else {
MediaStore.Images.Media.getBitmap(context.contentResolver, uri)
}
return bitmap
}
Upvotes: 0
Views: 1673
Reputation: 3315
The problem was in tmpImageUri
variable which was reset on orientation change. After saving and restoring it in savedInstance
everything started working as it should. Thanks @Selvin for help.
Fixed Main activity:
class MainActivity : AppCompatActivity() {
companion object {
private const val KEY_TMP_FILE_URI = "tmpFileUri"
}
private var tmpImageUri: Uri? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
tmpImageUri =
if(savedInstanceState?.containsKey(KEY_TMP_FILE_URI) == true)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
savedInstanceState.getParcelable(KEY_TMP_FILE_URI, Uri::class.java)
else savedInstanceState.getParcelable(KEY_TMP_FILE_URI)
else FileUtils.getTmpFileUri(applicationContext)
...
}
private val takeImageResult = registerForActivityResult(ActivityResultContracts.TakePicture()) { isSuccess ->
if (isSuccess) {
tmpImageUri?.let { launchEditActivity(it) }
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putParcelable(KEY_TMP_FILE_URI, tmpImageUri)
}
...
}
Upvotes: 1