Loading app/src/main/AndroidManifest.xml +1 −0 Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage" /> Loading app/src/main/java/app/fedilab/android/helper/Helper.java +7 −39 Original line number Diff line number Diff line Loading @@ -36,7 +36,6 @@ import android.content.pm.ResolveInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Color; Loading Loading @@ -157,7 +156,6 @@ import app.fedilab.android.client.entities.app.Timeline; import app.fedilab.android.databinding.PopupReleaseNotesBinding; import app.fedilab.android.exception.DBException; import app.fedilab.android.interfaces.OnDownloadInterface; import app.fedilab.android.sqlite.Sqlite; import app.fedilab.android.ui.drawer.ReleaseNoteAdapter; import app.fedilab.android.viewmodel.mastodon.AccountsVM; import app.fedilab.android.viewmodel.mastodon.OauthVM; Loading @@ -171,10 +169,6 @@ import okhttp3.RequestBody; public class Helper { public static final String TAG = "fedilab_app"; public static final String APP_CLIENT_ID = "APP_CLIENT_ID"; public static final String APP_CLIENT_SECRET = "APP_CLIENT_SECRET"; public static final String APP_INSTANCE = "APP_INSTANCE"; public static final String APP_API = "APP_API"; public static final String CLIP_BOARD = "CLIP_BOARD"; public static final String INSTANCE_SOCIAL_KEY = "jGj9gW3z9ptyIpB8CMGhAlTlslcemMV6AgoiImfw3vPP98birAJTHOWiu5ZWfCkLvcaLsFZw9e3Pb7TIwkbIyrj3z6S7r2oE6uy6EFHvls3YtapP8QKNZ980p9RfzTb4"; Loading Loading @@ -1244,18 +1238,14 @@ public class Helper { Date now = new Date(); attachment.filename = formatter.format(now) + "." + extension; if (attachment.mimeType.startsWith("image")) { try { final File certCacheDir = new File(context.getCacheDir(), TEMP_MEDIA_DIRECTORY); boolean isCertCacheDirExists = certCacheDir.exists(); if (!isCertCacheDirExists) { certCacheDir.mkdirs(); } String filePath = certCacheDir.getAbsolutePath() + "/" + attachment.filename; MediaHelper.ResizedImageRequestBody(context, uri, filePath); MediaHelper.ResizedImageRequestBody(context, uri, new File(filePath)); attachment.local_path = filePath; } catch (IOException e) { e.printStackTrace(); } } else { InputStream selectedFileInputStream; try { Loading Loading @@ -1616,28 +1606,6 @@ public class Helper { } } public static void transfertIfExist(Context context) { File dbFile = context.getDatabasePath(OLD_DB_NAME); if (!dbFile.exists()) { return; } int version = -1; try { SQLiteDatabase sqlDb = SQLiteDatabase.openDatabase (context.getDatabasePath(OLD_DB_NAME).getAbsolutePath(), null, SQLiteDatabase.OPEN_READONLY); version = sqlDb.getVersion(); } catch (Exception ignored) { } try { if (version == -1) { version = 38; } SQLiteDatabase oldDb = Sqlite.getInstance(context.getApplicationContext(), OLD_DB_NAME, null, version).open(); } catch (Exception ignored) { } context.deleteDatabase(OLD_DB_NAME); } public static String dateDiffFull(Date dateToot) { SimpleDateFormat df = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.MEDIUM, Locale.getDefault()); Loading app/src/main/java/app/fedilab/android/helper/MediaHelper.java +156 −149 Original line number Diff line number Diff line Loading @@ -21,26 +21,19 @@ import static app.fedilab.android.helper.LogoHelper.getMainLogo; import android.app.Activity; import android.app.DownloadManager; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.ImageDecoder; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.media.ExifInterface; import android.media.MediaRecorder; import android.media.MediaScannerConnection; import android.net.Uri; import android.os.Build; import android.os.Environment; import android.provider.MediaStore; import android.text.TextUtils; import android.text.format.DateFormat; import android.view.View; import android.webkit.MimeTypeMap; Loading @@ -51,7 +44,7 @@ import android.widget.Toast; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.core.content.FileProvider; import androidx.preference.PreferenceManager; import androidx.exifinterface.media.ExifInterface; import com.bumptech.glide.Glide; import com.bumptech.glide.request.target.CustomTarget; Loading @@ -62,6 +55,7 @@ import org.jetbrains.annotations.NotNull; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; Loading @@ -84,7 +78,6 @@ import app.fedilab.android.client.entities.api.Attachment; import app.fedilab.android.databinding.DatetimePickerBinding; import app.fedilab.android.databinding.PopupRecordBinding; import es.dmoral.toasty.Toasty; import okhttp3.MediaType; public class MediaHelper { Loading @@ -96,7 +89,6 @@ public class MediaHelper { * @param url String download url */ public static long manageDownloadsNoPopup(final Context context, final String url) { final SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context); final DownloadManager.Request request; try { Loading @@ -120,7 +112,11 @@ public class MediaHelper { } if (!new File(myDir).exists()) { new File(myDir).mkdir(); boolean created = new File(myDir).mkdir(); if (!created) { Toasty.error(context, context.getString(R.string.toast_error), Toasty.LENGTH_SHORT).show(); return -1; } } if (mime.toLowerCase().startsWith("video")) { request.setDestinationInExternalPublicDir(Environment.DIRECTORY_MOVIES, context.getString(R.string.app_name) + "/" + fileName); Loading Loading @@ -160,7 +156,11 @@ public class MediaHelper { File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); File targeted_folder = new File(path, context.getString(R.string.app_name)); if (!targeted_folder.exists()) { targeted_folder.mkdir(); boolean created = targeted_folder.mkdir(); if (!created) { Toasty.error(context, context.getString(R.string.toast_error), Toasty.LENGTH_SHORT).show(); return; } } FileInputStream fis = null; FileOutputStream fos = null; Loading Loading @@ -229,20 +229,6 @@ public class MediaHelper { }); } public static String formatSeconds(int seconds) { return getTwoDecimalsValue(seconds / 3600) + ":" + getTwoDecimalsValue(seconds / 60) + ":" + getTwoDecimalsValue(seconds % 60); } private static String getTwoDecimalsValue(int value) { if (value >= 0 && value <= 9) { return "0" + value; } else { return value + ""; } } public static String getMimeType(String url) { String type = null; Loading Loading @@ -284,14 +270,12 @@ public class MediaHelper { String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.ENGLISH).format(new Date()); String imageFileName = "JPEG_" + timeStamp + "_"; File storageDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES); File image = File.createTempFile( // Save a file: path for use with ACTION_VIEW intents return File.createTempFile( imageFileName, /* prefix */ ".jpg", /* suffix */ storageDir /* directory */ ); // Save a file: path for use with ACTION_VIEW intents String mCurrentPhotoPath = image.getAbsolutePath(); return image; } /** Loading Loading @@ -412,6 +396,7 @@ public class MediaHelper { * @param attachmentList - List<Attachment> * @return int - The max height */ @SuppressWarnings("unused") public static int returnMaxHeightForPreviews(Context context, List<Attachment> attachmentList) { int maxHeight = RelativeLayout.LayoutParams.WRAP_CONTENT; if (attachmentList != null && attachmentList.size() > 0) { Loading @@ -433,145 +418,167 @@ public class MediaHelper { void scheduledAt(String scheduledDate); } public static void ResizedImageRequestBody(Context context, Uri uri, String fullpatch) throws IOException { BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inJustDecodeBounds = true; String contentType; if ("file".equals(uri.getScheme())) { BitmapFactory.decodeFile(uri.getPath(), opts); contentType = MediaHelper.getFileMediaType(new File(uri.getPath())).type(); } else { try (InputStream in = context.getContentResolver().openInputStream(uri)) { BitmapFactory.decodeStream(in, null, opts); } contentType = context.getContentResolver().getType(uri); } if (TextUtils.isEmpty(contentType)) contentType = "image/jpeg"; Bitmap bitmap; if (Build.VERSION.SDK_INT >= 28) { ImageDecoder.Source source; if ("file".equals(uri.getScheme())) { source = ImageDecoder.createSource(new File(uri.getPath())); } else { source = ImageDecoder.createSource(context.getContentResolver(), uri); } BitmapFactory.Options finalOpts = opts; bitmap = ImageDecoder.decodeBitmap(source, (decoder, info, _source) -> { int[] size = getTargetSize(info.getSize().getWidth(), info.getSize().getHeight()); decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE); if (needResize(finalOpts.outWidth, finalOpts.outHeight)) { decoder.setTargetSize(size[0], size[1]); public static void ResizedImageRequestBody(Context context, Uri uri, File targetedFile) { InputStream decodeBitmapInputStream = null; try { InputStream inputStream = context.getContentResolver().openInputStream(uri); BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeStream(inputStream, null, options); try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } }); } else { int[] size = getTargetSize(opts.outWidth, opts.outHeight); int targetWidth; int targetHeight; if (needResize(opts.outWidth, opts.outHeight)) { targetWidth = size[0]; targetHeight = size[1]; int orientation = getImageOrientation(uri, context.getContentResolver()); int scaledImageSize = 1024; do { FileOutputStream outputStream = new FileOutputStream(targetedFile); decodeBitmapInputStream = context.getContentResolver().openInputStream(uri); options.inSampleSize = calculateInSampleSize(options, scaledImageSize, scaledImageSize); options.inJustDecodeBounds = false; Bitmap scaledBitmap = BitmapFactory.decodeStream(decodeBitmapInputStream, null, options); Bitmap reorientedBitmap = reorientBitmap(scaledBitmap, orientation); if (reorientedBitmap == null) { scaledBitmap.recycle(); return; } Bitmap.CompressFormat format; if (!reorientedBitmap.hasAlpha()) { format = Bitmap.CompressFormat.JPEG; } else { targetWidth = opts.outWidth; targetHeight = opts.outHeight; } float factor = opts.outWidth / (float) targetWidth; opts = new BitmapFactory.Options(); opts.inSampleSize = (int) factor; int orientation = 0; String[] projection = {MediaStore.Images.ImageColumns.ORIENTATION}; try { Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null); if (cursor.moveToFirst()) { int photoRotation = cursor.getInt(0); format = Bitmap.CompressFormat.PNG; } cursor.close(); reorientedBitmap.compress(format, 100, outputStream); reorientedBitmap.recycle(); scaledImageSize /= 2; } while (targetedFile.length() > getMaxSize(targetedFile.length())); } catch (Exception e) { e.printStackTrace(); if (decodeBitmapInputStream != null) { try { decodeBitmapInputStream.close(); } catch (IOException ex) { ex.printStackTrace(); } if ("file".equals(uri.getScheme())) { ExifInterface exif = new ExifInterface(uri.getPath()); orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { try (InputStream in = context.getContentResolver().openInputStream(uri)) { ExifInterface exif = new ExifInterface(in); orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); } } if ("file".equals(uri.getScheme())) { bitmap = BitmapFactory.decodeFile(uri.getPath(), opts); } else { try (InputStream in = context.getContentResolver().openInputStream(uri)) { bitmap = BitmapFactory.decodeStream(in, null, opts); } } if (factor % 1f != 0f) { Rect srcBounds = null; Rect dstBounds; dstBounds = new Rect(0, 0, targetWidth, targetHeight); Bitmap scaled = Bitmap.createBitmap(dstBounds.width(), dstBounds.height(), Bitmap.Config.ARGB_8888); new Canvas(scaled).drawBitmap(bitmap, srcBounds, dstBounds, new Paint(Paint.FILTER_BITMAP_FLAG)); bitmap = scaled; } private static int calculateInSampleSize(BitmapFactory.Options options, int rqWidth, int rqHeight) { int height = options.outHeight; int width = options.outWidth; int inSampleSize = 1; if (height > rqHeight || width > rqWidth) { int rotation = 0; switch (orientation) { case ExifInterface.ORIENTATION_ROTATE_90: rotation = 90; break; case ExifInterface.ORIENTATION_ROTATE_180: rotation = 180; break; case ExifInterface.ORIENTATION_ROTATE_270: rotation = 270; break; int halfHeight = height / 2; int halfWidth = width / 2; while ((halfHeight / inSampleSize) > rqHeight && (halfWidth / inSampleSize) > rqWidth) { inSampleSize *= 2; } if (rotation != 0) { Matrix matrix = new Matrix(); matrix.setRotate(rotation); bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false); } return inSampleSize; } boolean isPNG = "image/png".equals(contentType); File tempFile = new File(fullpatch); try (FileOutputStream out = new FileOutputStream(tempFile)) { if (isPNG) { bitmap.compress(Bitmap.CompressFormat.PNG, 0, out); private static int getImageOrientation(Uri uri, ContentResolver contentResolver) { InputStream inputStream; try { inputStream = contentResolver.openInputStream(uri); } catch (FileNotFoundException e) { e.printStackTrace(); return ExifInterface.ORIENTATION_UNDEFINED; } if (inputStream == null) { return ExifInterface.ORIENTATION_UNDEFINED; } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { try { ExifInterface exifInterface = new ExifInterface(inputStream); int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); inputStream.close(); return orientation; } catch (IOException e) { try { inputStream.close(); } catch (IOException ex) { ex.printStackTrace(); return ExifInterface.ORIENTATION_UNDEFINED; } e.printStackTrace(); return ExifInterface.ORIENTATION_UNDEFINED; } } else { bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out); try { ExifInterface exifInterface = new ExifInterface(uri.getPath()); int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); inputStream.close(); return orientation; } catch (IOException e) { try { inputStream.close(); } catch (IOException ex) { ex.printStackTrace(); return ExifInterface.ORIENTATION_UNDEFINED; } e.printStackTrace(); return ExifInterface.ORIENTATION_UNDEFINED; } } } private static int[] getTargetSize(int srcWidth, int srcHeight) { int maxSize = 1; private static long getMaxSize(long maxSize) { if (MainActivity.instanceInfo != null && MainActivity.instanceInfo.configuration != null && MainActivity.instanceInfo.configuration.media_attachments != null) { maxSize = MainActivity.instanceInfo.configuration.media_attachments.image_size_limit; } int targetWidth = Math.round((float) Math.sqrt((float) maxSize * ((float) srcWidth / srcHeight))); int targetHeight = Math.round((float) Math.sqrt((float) maxSize * ((float) srcHeight / srcWidth))); return new int[]{targetWidth, targetHeight}; return maxSize; } private static boolean needResize(int srcWidth, int srcHeight) { int maxSize; if (MainActivity.instanceInfo != null && MainActivity.instanceInfo.configuration != null && MainActivity.instanceInfo.configuration.media_attachments != null) { maxSize = MainActivity.instanceInfo.configuration.media_attachments.image_size_limit; } else { return false; public static Bitmap reorientBitmap(Bitmap bitmap, int orientation) { Matrix matrix = new Matrix(); switch (orientation) { case ExifInterface.ORIENTATION_FLIP_HORIZONTAL: matrix.setScale(-1.0f, 1.0f); break; case ExifInterface.ORIENTATION_ROTATE_180: matrix.setRotate(180.0f); break; case ExifInterface.ORIENTATION_FLIP_VERTICAL: matrix.setRotate(180.0f); matrix.postScale(-1.0f, 1.0f); break; case ExifInterface.ORIENTATION_TRANSPOSE: matrix.setRotate(90.0f); matrix.postScale(-1.0f, 1.0f); break; case ExifInterface.ORIENTATION_ROTATE_90: matrix.setRotate(90.0f); break; case ExifInterface.ORIENTATION_TRANSVERSE: matrix.setRotate(-90.0f); matrix.postScale(-1.0f, 1.0f); break; case ExifInterface.ORIENTATION_ROTATE_270: matrix.setRotate(-90.0f); break; default: return bitmap; } return srcWidth * srcHeight > maxSize; if (bitmap == null) { return null; } public static MediaType getFileMediaType(File file) { String name = file.getName(); return MediaType.parse(MimeTypeMap.getSingleton().getMimeTypeFromExtension(name.substring(name.lastIndexOf('.') + 1))); try { Bitmap result = Bitmap.createBitmap( bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); if (!bitmap.sameAs(result)) { bitmap.recycle(); } return result; } catch (Exception e) { e.printStackTrace(); } return null; } } Loading
app/src/main/AndroidManifest.xml +1 −0 Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage" /> Loading
app/src/main/java/app/fedilab/android/helper/Helper.java +7 −39 Original line number Diff line number Diff line Loading @@ -36,7 +36,6 @@ import android.content.pm.ResolveInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Color; Loading Loading @@ -157,7 +156,6 @@ import app.fedilab.android.client.entities.app.Timeline; import app.fedilab.android.databinding.PopupReleaseNotesBinding; import app.fedilab.android.exception.DBException; import app.fedilab.android.interfaces.OnDownloadInterface; import app.fedilab.android.sqlite.Sqlite; import app.fedilab.android.ui.drawer.ReleaseNoteAdapter; import app.fedilab.android.viewmodel.mastodon.AccountsVM; import app.fedilab.android.viewmodel.mastodon.OauthVM; Loading @@ -171,10 +169,6 @@ import okhttp3.RequestBody; public class Helper { public static final String TAG = "fedilab_app"; public static final String APP_CLIENT_ID = "APP_CLIENT_ID"; public static final String APP_CLIENT_SECRET = "APP_CLIENT_SECRET"; public static final String APP_INSTANCE = "APP_INSTANCE"; public static final String APP_API = "APP_API"; public static final String CLIP_BOARD = "CLIP_BOARD"; public static final String INSTANCE_SOCIAL_KEY = "jGj9gW3z9ptyIpB8CMGhAlTlslcemMV6AgoiImfw3vPP98birAJTHOWiu5ZWfCkLvcaLsFZw9e3Pb7TIwkbIyrj3z6S7r2oE6uy6EFHvls3YtapP8QKNZ980p9RfzTb4"; Loading Loading @@ -1244,18 +1238,14 @@ public class Helper { Date now = new Date(); attachment.filename = formatter.format(now) + "." + extension; if (attachment.mimeType.startsWith("image")) { try { final File certCacheDir = new File(context.getCacheDir(), TEMP_MEDIA_DIRECTORY); boolean isCertCacheDirExists = certCacheDir.exists(); if (!isCertCacheDirExists) { certCacheDir.mkdirs(); } String filePath = certCacheDir.getAbsolutePath() + "/" + attachment.filename; MediaHelper.ResizedImageRequestBody(context, uri, filePath); MediaHelper.ResizedImageRequestBody(context, uri, new File(filePath)); attachment.local_path = filePath; } catch (IOException e) { e.printStackTrace(); } } else { InputStream selectedFileInputStream; try { Loading Loading @@ -1616,28 +1606,6 @@ public class Helper { } } public static void transfertIfExist(Context context) { File dbFile = context.getDatabasePath(OLD_DB_NAME); if (!dbFile.exists()) { return; } int version = -1; try { SQLiteDatabase sqlDb = SQLiteDatabase.openDatabase (context.getDatabasePath(OLD_DB_NAME).getAbsolutePath(), null, SQLiteDatabase.OPEN_READONLY); version = sqlDb.getVersion(); } catch (Exception ignored) { } try { if (version == -1) { version = 38; } SQLiteDatabase oldDb = Sqlite.getInstance(context.getApplicationContext(), OLD_DB_NAME, null, version).open(); } catch (Exception ignored) { } context.deleteDatabase(OLD_DB_NAME); } public static String dateDiffFull(Date dateToot) { SimpleDateFormat df = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.MEDIUM, Locale.getDefault()); Loading
app/src/main/java/app/fedilab/android/helper/MediaHelper.java +156 −149 Original line number Diff line number Diff line Loading @@ -21,26 +21,19 @@ import static app.fedilab.android.helper.LogoHelper.getMainLogo; import android.app.Activity; import android.app.DownloadManager; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.ImageDecoder; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.media.ExifInterface; import android.media.MediaRecorder; import android.media.MediaScannerConnection; import android.net.Uri; import android.os.Build; import android.os.Environment; import android.provider.MediaStore; import android.text.TextUtils; import android.text.format.DateFormat; import android.view.View; import android.webkit.MimeTypeMap; Loading @@ -51,7 +44,7 @@ import android.widget.Toast; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.core.content.FileProvider; import androidx.preference.PreferenceManager; import androidx.exifinterface.media.ExifInterface; import com.bumptech.glide.Glide; import com.bumptech.glide.request.target.CustomTarget; Loading @@ -62,6 +55,7 @@ import org.jetbrains.annotations.NotNull; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; Loading @@ -84,7 +78,6 @@ import app.fedilab.android.client.entities.api.Attachment; import app.fedilab.android.databinding.DatetimePickerBinding; import app.fedilab.android.databinding.PopupRecordBinding; import es.dmoral.toasty.Toasty; import okhttp3.MediaType; public class MediaHelper { Loading @@ -96,7 +89,6 @@ public class MediaHelper { * @param url String download url */ public static long manageDownloadsNoPopup(final Context context, final String url) { final SharedPreferences sharedpreferences = PreferenceManager.getDefaultSharedPreferences(context); final DownloadManager.Request request; try { Loading @@ -120,7 +112,11 @@ public class MediaHelper { } if (!new File(myDir).exists()) { new File(myDir).mkdir(); boolean created = new File(myDir).mkdir(); if (!created) { Toasty.error(context, context.getString(R.string.toast_error), Toasty.LENGTH_SHORT).show(); return -1; } } if (mime.toLowerCase().startsWith("video")) { request.setDestinationInExternalPublicDir(Environment.DIRECTORY_MOVIES, context.getString(R.string.app_name) + "/" + fileName); Loading Loading @@ -160,7 +156,11 @@ public class MediaHelper { File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); File targeted_folder = new File(path, context.getString(R.string.app_name)); if (!targeted_folder.exists()) { targeted_folder.mkdir(); boolean created = targeted_folder.mkdir(); if (!created) { Toasty.error(context, context.getString(R.string.toast_error), Toasty.LENGTH_SHORT).show(); return; } } FileInputStream fis = null; FileOutputStream fos = null; Loading Loading @@ -229,20 +229,6 @@ public class MediaHelper { }); } public static String formatSeconds(int seconds) { return getTwoDecimalsValue(seconds / 3600) + ":" + getTwoDecimalsValue(seconds / 60) + ":" + getTwoDecimalsValue(seconds % 60); } private static String getTwoDecimalsValue(int value) { if (value >= 0 && value <= 9) { return "0" + value; } else { return value + ""; } } public static String getMimeType(String url) { String type = null; Loading Loading @@ -284,14 +270,12 @@ public class MediaHelper { String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.ENGLISH).format(new Date()); String imageFileName = "JPEG_" + timeStamp + "_"; File storageDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES); File image = File.createTempFile( // Save a file: path for use with ACTION_VIEW intents return File.createTempFile( imageFileName, /* prefix */ ".jpg", /* suffix */ storageDir /* directory */ ); // Save a file: path for use with ACTION_VIEW intents String mCurrentPhotoPath = image.getAbsolutePath(); return image; } /** Loading Loading @@ -412,6 +396,7 @@ public class MediaHelper { * @param attachmentList - List<Attachment> * @return int - The max height */ @SuppressWarnings("unused") public static int returnMaxHeightForPreviews(Context context, List<Attachment> attachmentList) { int maxHeight = RelativeLayout.LayoutParams.WRAP_CONTENT; if (attachmentList != null && attachmentList.size() > 0) { Loading @@ -433,145 +418,167 @@ public class MediaHelper { void scheduledAt(String scheduledDate); } public static void ResizedImageRequestBody(Context context, Uri uri, String fullpatch) throws IOException { BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inJustDecodeBounds = true; String contentType; if ("file".equals(uri.getScheme())) { BitmapFactory.decodeFile(uri.getPath(), opts); contentType = MediaHelper.getFileMediaType(new File(uri.getPath())).type(); } else { try (InputStream in = context.getContentResolver().openInputStream(uri)) { BitmapFactory.decodeStream(in, null, opts); } contentType = context.getContentResolver().getType(uri); } if (TextUtils.isEmpty(contentType)) contentType = "image/jpeg"; Bitmap bitmap; if (Build.VERSION.SDK_INT >= 28) { ImageDecoder.Source source; if ("file".equals(uri.getScheme())) { source = ImageDecoder.createSource(new File(uri.getPath())); } else { source = ImageDecoder.createSource(context.getContentResolver(), uri); } BitmapFactory.Options finalOpts = opts; bitmap = ImageDecoder.decodeBitmap(source, (decoder, info, _source) -> { int[] size = getTargetSize(info.getSize().getWidth(), info.getSize().getHeight()); decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE); if (needResize(finalOpts.outWidth, finalOpts.outHeight)) { decoder.setTargetSize(size[0], size[1]); public static void ResizedImageRequestBody(Context context, Uri uri, File targetedFile) { InputStream decodeBitmapInputStream = null; try { InputStream inputStream = context.getContentResolver().openInputStream(uri); BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeStream(inputStream, null, options); try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } }); } else { int[] size = getTargetSize(opts.outWidth, opts.outHeight); int targetWidth; int targetHeight; if (needResize(opts.outWidth, opts.outHeight)) { targetWidth = size[0]; targetHeight = size[1]; int orientation = getImageOrientation(uri, context.getContentResolver()); int scaledImageSize = 1024; do { FileOutputStream outputStream = new FileOutputStream(targetedFile); decodeBitmapInputStream = context.getContentResolver().openInputStream(uri); options.inSampleSize = calculateInSampleSize(options, scaledImageSize, scaledImageSize); options.inJustDecodeBounds = false; Bitmap scaledBitmap = BitmapFactory.decodeStream(decodeBitmapInputStream, null, options); Bitmap reorientedBitmap = reorientBitmap(scaledBitmap, orientation); if (reorientedBitmap == null) { scaledBitmap.recycle(); return; } Bitmap.CompressFormat format; if (!reorientedBitmap.hasAlpha()) { format = Bitmap.CompressFormat.JPEG; } else { targetWidth = opts.outWidth; targetHeight = opts.outHeight; } float factor = opts.outWidth / (float) targetWidth; opts = new BitmapFactory.Options(); opts.inSampleSize = (int) factor; int orientation = 0; String[] projection = {MediaStore.Images.ImageColumns.ORIENTATION}; try { Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null); if (cursor.moveToFirst()) { int photoRotation = cursor.getInt(0); format = Bitmap.CompressFormat.PNG; } cursor.close(); reorientedBitmap.compress(format, 100, outputStream); reorientedBitmap.recycle(); scaledImageSize /= 2; } while (targetedFile.length() > getMaxSize(targetedFile.length())); } catch (Exception e) { e.printStackTrace(); if (decodeBitmapInputStream != null) { try { decodeBitmapInputStream.close(); } catch (IOException ex) { ex.printStackTrace(); } if ("file".equals(uri.getScheme())) { ExifInterface exif = new ExifInterface(uri.getPath()); orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { try (InputStream in = context.getContentResolver().openInputStream(uri)) { ExifInterface exif = new ExifInterface(in); orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); } } if ("file".equals(uri.getScheme())) { bitmap = BitmapFactory.decodeFile(uri.getPath(), opts); } else { try (InputStream in = context.getContentResolver().openInputStream(uri)) { bitmap = BitmapFactory.decodeStream(in, null, opts); } } if (factor % 1f != 0f) { Rect srcBounds = null; Rect dstBounds; dstBounds = new Rect(0, 0, targetWidth, targetHeight); Bitmap scaled = Bitmap.createBitmap(dstBounds.width(), dstBounds.height(), Bitmap.Config.ARGB_8888); new Canvas(scaled).drawBitmap(bitmap, srcBounds, dstBounds, new Paint(Paint.FILTER_BITMAP_FLAG)); bitmap = scaled; } private static int calculateInSampleSize(BitmapFactory.Options options, int rqWidth, int rqHeight) { int height = options.outHeight; int width = options.outWidth; int inSampleSize = 1; if (height > rqHeight || width > rqWidth) { int rotation = 0; switch (orientation) { case ExifInterface.ORIENTATION_ROTATE_90: rotation = 90; break; case ExifInterface.ORIENTATION_ROTATE_180: rotation = 180; break; case ExifInterface.ORIENTATION_ROTATE_270: rotation = 270; break; int halfHeight = height / 2; int halfWidth = width / 2; while ((halfHeight / inSampleSize) > rqHeight && (halfWidth / inSampleSize) > rqWidth) { inSampleSize *= 2; } if (rotation != 0) { Matrix matrix = new Matrix(); matrix.setRotate(rotation); bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false); } return inSampleSize; } boolean isPNG = "image/png".equals(contentType); File tempFile = new File(fullpatch); try (FileOutputStream out = new FileOutputStream(tempFile)) { if (isPNG) { bitmap.compress(Bitmap.CompressFormat.PNG, 0, out); private static int getImageOrientation(Uri uri, ContentResolver contentResolver) { InputStream inputStream; try { inputStream = contentResolver.openInputStream(uri); } catch (FileNotFoundException e) { e.printStackTrace(); return ExifInterface.ORIENTATION_UNDEFINED; } if (inputStream == null) { return ExifInterface.ORIENTATION_UNDEFINED; } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { try { ExifInterface exifInterface = new ExifInterface(inputStream); int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); inputStream.close(); return orientation; } catch (IOException e) { try { inputStream.close(); } catch (IOException ex) { ex.printStackTrace(); return ExifInterface.ORIENTATION_UNDEFINED; } e.printStackTrace(); return ExifInterface.ORIENTATION_UNDEFINED; } } else { bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out); try { ExifInterface exifInterface = new ExifInterface(uri.getPath()); int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); inputStream.close(); return orientation; } catch (IOException e) { try { inputStream.close(); } catch (IOException ex) { ex.printStackTrace(); return ExifInterface.ORIENTATION_UNDEFINED; } e.printStackTrace(); return ExifInterface.ORIENTATION_UNDEFINED; } } } private static int[] getTargetSize(int srcWidth, int srcHeight) { int maxSize = 1; private static long getMaxSize(long maxSize) { if (MainActivity.instanceInfo != null && MainActivity.instanceInfo.configuration != null && MainActivity.instanceInfo.configuration.media_attachments != null) { maxSize = MainActivity.instanceInfo.configuration.media_attachments.image_size_limit; } int targetWidth = Math.round((float) Math.sqrt((float) maxSize * ((float) srcWidth / srcHeight))); int targetHeight = Math.round((float) Math.sqrt((float) maxSize * ((float) srcHeight / srcWidth))); return new int[]{targetWidth, targetHeight}; return maxSize; } private static boolean needResize(int srcWidth, int srcHeight) { int maxSize; if (MainActivity.instanceInfo != null && MainActivity.instanceInfo.configuration != null && MainActivity.instanceInfo.configuration.media_attachments != null) { maxSize = MainActivity.instanceInfo.configuration.media_attachments.image_size_limit; } else { return false; public static Bitmap reorientBitmap(Bitmap bitmap, int orientation) { Matrix matrix = new Matrix(); switch (orientation) { case ExifInterface.ORIENTATION_FLIP_HORIZONTAL: matrix.setScale(-1.0f, 1.0f); break; case ExifInterface.ORIENTATION_ROTATE_180: matrix.setRotate(180.0f); break; case ExifInterface.ORIENTATION_FLIP_VERTICAL: matrix.setRotate(180.0f); matrix.postScale(-1.0f, 1.0f); break; case ExifInterface.ORIENTATION_TRANSPOSE: matrix.setRotate(90.0f); matrix.postScale(-1.0f, 1.0f); break; case ExifInterface.ORIENTATION_ROTATE_90: matrix.setRotate(90.0f); break; case ExifInterface.ORIENTATION_TRANSVERSE: matrix.setRotate(-90.0f); matrix.postScale(-1.0f, 1.0f); break; case ExifInterface.ORIENTATION_ROTATE_270: matrix.setRotate(-90.0f); break; default: return bitmap; } return srcWidth * srcHeight > maxSize; if (bitmap == null) { return null; } public static MediaType getFileMediaType(File file) { String name = file.getName(); return MediaType.parse(MimeTypeMap.getSingleton().getMimeTypeFromExtension(name.substring(name.lastIndexOf('.') + 1))); try { Bitmap result = Bitmap.createBitmap( bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); if (!bitmap.sameAs(result)) { bitmap.recycle(); } return result; } catch (Exception e) { e.printStackTrace(); } return null; } }