I have made image compression long ago. It was working well until Android X, but above Android it’s not working.
public class ImageCompress {
private static final int IMAGE_QUALITY = 99;
private static final int MAX_SIZE_PIXELS = 1200;
public static Bitmap decodeFile(File file, int width, int height) {
try {
// Decode image size
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(file), null, options);
// The new size we want to scale to
final int REQUIRED_WIDTH = width;
final int REQUIRED_HIGHT = height;
// Find the correct scale value. It should be the power of 2.
int scale = 1;
while (options.outWidth / scale / 2 >= REQUIRED_WIDTH
&& options.outHeight / scale / 2 >= REQUIRED_HIGHT)
scale *= 2;
// Decode with inSampleSize
BitmapFactory.Options newOptions = new BitmapFactory.Options();
newOptions.inSampleSize = scale;
return BitmapFactory.decodeStream(new FileInputStream(file), null, newOptions);
} catch (FileNotFoundException e) {
return null;
}
}
public static File compressImage(final File file, String outputDirectory, Context context) {
String path = file.getPath();
String format = path.substring(path.lastIndexOf(".")).substring(1);
Bitmap source;
try {
source = decodeFile(file, MAX_SIZE_PIXELS, MAX_SIZE_PIXELS);
} catch (Exception e) {
Log.d(ImageCompress.class.toString(), e.toString() + " Something happened wrong");
return null;
}
Bitmap.CompressFormat compressFormat;
// If PNG or WebP have the allowed resolution, then do not compress it
if ("png".equals(format) || "webp".equals(format)) return file;
// Select format
switch (format) {
case "png":
compressFormat = Bitmap.CompressFormat.PNG;
break;
case "webp":
compressFormat = Bitmap.CompressFormat.WEBP;
break;
case "gif":
return file;
default:
compressFormat = Bitmap.CompressFormat.JPEG;
}
// Resize image
Bitmap resizedBmp;
// I am getting an error on the source.getHeight() line
if (source.getHeight() > MAX_SIZE_PIXELS || source.getWidth() > MAX_SIZE_PIXELS) {
resizedBmp = resizeBitmap(source, MAX_SIZE_PIXELS);
} else {
resizedBmp = source;
}
File result = null;
OutputStream fOut = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
ContentResolver resolver = BaseApplication.getInstance().getContentResolver();
ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, file.getName());
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/" + format);
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Constants.MULTIMEDIA_CHATROOM_11);
Uri imageUri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
try {
fOut = resolver.openOutputStream(imageUri);
result = new File(Utils.getPath(BaseApplication.getInstance(), imageUri));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
} else {
// create directory if not exist
File directory = new File(outputDirectory);
int code = context.getPackageManager().checkPermission(
android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
context.getPackageName());
if (code == PackageManager.PERMISSION_GRANTED) {
Log.i(ImageCompress.class.getSimpleName(), "creating directory");
if (!directory.isDirectory() || !directory.exists()) {
directory.mkdirs();
}
} else {
Log.i(ImageCompress.class.getSimpleName(), "failed access");
}
// Compress image
result = new File(outputDirectory, file.getName());
try {
if (!result.exists()) {
result.createNewFile();
fOut = new FileOutputStream(result);
} else {
fOut = new FileOutputStream(result);
}
} catch (IOException e) {
e.printStackTrace();
}
}
try {
resizedBmp.compress(compressFormat, IMAGE_QUALITY, fOut);
fOut.flush();
fOut.close();
source.recycle();
resizedBmp.recycle();
} catch (Exception e) {
e.printStackTrace();
return null;
}
// Copy EXIF orientation from the original image
try {
ExifInterface oldExif = new ExifInterface(file.getPath());
String exifOrientation = oldExif.getAttribute(ExifInterface.TAG_ORIENTATION);
if (exifOrientation != null) {
ExifInterface newExif = new ExifInterface(result.getPath());
newExif.setAttribute(ExifInterface.TAG_ORIENTATION, exifOrientation);
newExif.saveAttributes();
}
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
private static Bitmap resizeBitmap(Bitmap source, int maxSizePixels) {
int targetWidth, targetHeight;
double aspectRatio;
if (source.getWidth() > source.getHeight()) {
targetWidth = maxSizePixels;
aspectRatio = (double) source.getHeight() / (double) source.getWidth();
targetHeight = (int) (targetWidth * aspectRatio);
} else {
targetHeight = maxSizePixels;
aspectRatio = (double) source.getWidth() / (double) source.getHeight();
targetWidth = (int) (targetHeight * aspectRatio);
}
return Bitmap.createScaledBitmap(source, targetWidth, targetHeight, false);
}
}
My app is made for social entertainment purposes, so I don't have storage access like before due to policies. So we can use only the session scope way to access files. I am getting an error at line source.getHeight() and source.getWidth() as both are zero. How can I fix this this issue for all Android versions?
Error:
Please note: I noticed this error comes in a few pictures from the gallery.
If you get a NullPointerException
on source.getHeight()
then this means source
has been set to null
.
The only point where source
is set is on the line
source = decodeFile(file, MAX_SIZE_PIXELS, MAX_SIZE_PIXELS);
This means the method decodeFile
has to return a null value.
Looking at the method decodeFile
you can see tha there are only two different code paths that return a value. The first code path returns the value of BitmapFactory.decodeStream(..)
which AFAIK should never return null
.
The it is most likely the other code path in the FileNotFoundException
catch clause.
public static Bitmap decodeFile(File file, int width, int height) {
try {
....
return BitmapFactory.decodeStream(new FileInputStream(file), null, newOptions);
} catch (FileNotFoundException e) {
return null;
}
}
This leads to the conclusion that at some point you are trying to read a File that does not exist or on that you don't have read permissions.
As your app is older the chance is high that if it is a permission problem and the file is on the shared storage (/sdcard
or subfolder) this is because the app does not make use of scoped storage system introduced with Android 10/11.