I wrote a library that has the following code. The purpose of this code is to get a Bitmap Object from a filepath and rotate it if needed:
private fun rotateBitmap(imageURI: String) : File {
val options = BitmapFactory.Options()
val bitmap = BitmapFactory.decodeFile(imageURI, options)
val rotation = getRotation() // some function, always returns null in this scenario
var newBitmap = if (rotation != 0f) {
//New rotation matrix
val matrix = Matrix()
matrix.preRotate(rotation)
//Bitmap.createBitmap(bitmap, 0, 0, 1000, 1000, matrix, true)
Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
} else {
//No need to rotate
BitmapFactory.decodeFile(imageURI, options)
}
// ..... do some more stuff
}
I use this library in three different Apps. The apps are almost identical, except for some resources like strings, colors etc. All of them work the same, opening the gallery to let the user choose a local picture and passing the filepath of that picture to this method as imageURI
parameter. This functionality is written once and used by all of them the same way, so differences in the implementation are impossible, as they share the same code.
In all three apps, I choose the same file, which makes it pass the same imageURI
for each of them. The rotation
is always 0, which means the library always executes the else
portion. Through debugging I know that the three apps run rotateBitmap
exactly the same, except for the return value of decodeFile
.
In App A
and B
it works as intended.
In App C
it always returns null
, for both bitmap
and newBitmap
, without throwing any Exception
.
All three Apps have the same Permissions.
Resources like Memory also behave pretty much exactly the same, because, as I said, the apps are almost identical.
I have checked the documentation and a few SO questions/answers:
- BitmapFactory.decodeFile() returns null
- Bitmap is returning null from BitmapFactory.decodeFile(filename)
- Android: decodeFile always returns null for file in internal storage
But none of them address or explain the seemingly nondeterministic behaviour.
Any idea on how to explain and/or solve this would help.
EDIT:
As suggested by CommonsWare, the error might occur where the imageURI
is determined. This part also works exactly the same in all three apps and results in the same imageURI
in all three of them. So here I include the code that determines it:
Starting Activity for Picking Image
(After getting permission)
val intent = Intent()
intent.type = "image/*"
intent.action = Intent.ACTION_GET_CONTENT
intent.putExtra(
MediaStore.EXTRA_OUTPUT,
Uri.fromFile(File(getExternalStorageDirectory(), "chosenImage"))
)
val photoIntent = Intent.createChooser(intent, "Select Picture")
startActivityForResult(photoIntent, PickImage)
Processing Activity response
uri = "content://com.android.providers.media.documents/document/image%3A24655"
final String docId = DocumentsContract.getDocumentId(uri);
final String [] split = docId . split (":");
final String type = split[0];
Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
final String selection = "_id=?";
final String [] selectionArgs = new String[]{
split[1]
};
return getDataColumn(context, contentUri, selection, selectionArgs);
uri = "content://media/external/images/media"
selection = "_id=?"
selectionArgs = ["24655"]
public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs)
{
Cursor cursor = null;
final String column = "_data";
final String [] projection = {
column
};
try {
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
if (cursor != null && cursor.moveToFirst()) {
final int column_index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(column_index);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
The resulting imageURI
looks like this = /storage/emulated/0/Download/tenor (1).gif
Its a downloaded image.
EDIT2:
I didn't notice that App C
, which had the unwanted behaviour, was compiled with compileSdkVersion 29
and targetSdkVersion 29
while the others were compiled with 28/28
.
So I tried compiling with compileSdkVersion 29
and targetSdkVersion 28
. Which made it work. However, when I have overwritten with 29/29
, it also worked.
After uninstalling and compiling with compileSdkVersion 29
and targetSdkVersion 29
from the beginning, it again, didn't work.
So.
- 28/28 - works
- 29/29 - doesn't work
- 29/28 - works
- 29/28 & overwrite with 29/29 - works
- 29/28, uninstall, reinstall with 29/29 - doesn't work
Still need to know why and how this behavior occurs, so that I can update in the future.