1

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:

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.

halfer
  • 19,824
  • 17
  • 99
  • 186
Benjamin Basmaci
  • 2,247
  • 2
  • 25
  • 46
  • "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" -- most likely, this is the source of your difficulty. See https://stackoverflow.com/q/54843895/115145 and https://stackoverflow.com/a/59123287/115145 and https://stackoverflow.com/q/49221312/115145. – CommonsWare May 08 '20 at 16:15
  • Strange behavior, while all the sources being the same. – Animesh Sahu May 08 '20 at 16:20
  • @CommonsWare As I said, the value for `imageURI` is the same in all three cases. – Benjamin Basmaci May 08 '20 at 16:21
  • That does not imply that `imageURI` was obtained correctly. What exactly is the value of `imageURI`, and how are you obtaining it? – CommonsWare May 08 '20 at 16:25
  • @CommonsWare I have updated the question. I couldnt quite figure out whats wrong with the links you provided. – Benjamin Basmaci May 13 '20 at 12:37

0 Answers0