From 69daf0bf18ecad4eadf92fb388466e539dd9512c Mon Sep 17 00:00:00 2001 From: Yasir Modak <42785357+ymodak@users.noreply.github.com> Date: Thu, 22 May 2025 12:35:43 -0700 Subject: [PATCH 1/8] Update bug_report.md --- .github/ISSUE_TEMPLATE/bug_report.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 4908d31..2472859 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -7,16 +7,16 @@ assignees: '' --- -**Describe the bug** +**Describe the bug:** A clear and concise description of what the bug is. -**To Reproduce** +**To Reproduce:** Steps to reproduce the behavior: -**Expected behavior** +**Expected behavior:** A clear and concise description of what you expected to happen. -**Screenshots** +**Screenshots:** If applicable, add screenshots to help explain your problem. **Desktop (please complete the following information):** @@ -30,5 +30,5 @@ If applicable, add screenshots to help explain your problem. - Browser [e.g. stock browser, safari] - Version [e.g. 22] -**Additional context** +**Additional context:** Add any other context about the problem here. From c47f8e381a7be83de6cc52d8b6b75c73d69c3959 Mon Sep 17 00:00:00 2001 From: Yasir Modak <42785357+ymodak@users.noreply.github.com> Date: Thu, 22 May 2025 12:36:32 -0700 Subject: [PATCH 2/8] Update feature_request.md --- .github/ISSUE_TEMPLATE/feature_request.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index bbcbbe7..2b7a665 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -7,14 +7,14 @@ assignees: '' --- -**Is your feature request related to a problem? Please describe.** +**Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -**Describe the solution you'd like** +**Describe the solution you'd like** A clear and concise description of what you want to happen. -**Describe alternatives you've considered** +**Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. -**Additional context** +**Additional context** Add any other context or screenshots about the feature request here. From 28bcb4d38c4ecd59c0180cee5f95c9647436ccc7 Mon Sep 17 00:00:00 2001 From: Yasir Modak <42785357+ymodak@users.noreply.github.com> Date: Thu, 22 May 2025 12:45:57 -0700 Subject: [PATCH 3/8] Create support_request.md --- .github/ISSUE_TEMPLATE/support_request.md | 46 +++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/support_request.md diff --git a/.github/ISSUE_TEMPLATE/support_request.md b/.github/ISSUE_TEMPLATE/support_request.md new file mode 100644 index 0000000..ebde6c4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/support_request.md @@ -0,0 +1,46 @@ +--- +name: 🆘 Support Request +about: Ask a question or get help with usage. +title: "[Support]: " +labels: ["support", "question"] +assignees: [] +--- + + + +**What do you need help with?** + +Is this a question about how to do something, a configuration problem, or a general issue you can't solve?* + +**Describe the issue/question:** + +Clearly describe what you are trying to achieve, what problem you are facing, or what question you have. + +**What have you tried so far? (Optional):** + +List any steps you've already taken to troubleshoot, find information, or attempt a solution.* + +**Expected outcome (Optional):** + +If applicable, what did you hope would happen, or what solution are you looking for?* + +**Screenshots/Videos (Optional):** + +*If applicable, add screenshots or a short video that might help explain your situation.* + +**Environment & Details:** + +Please provide details about your operating environment, relevant URLs, or any messages you see.* + +- **Operating System:** +- **Browser & Version (if applicable):** +- **Any relevant messages (e.g., from UI, console):** + ``` + PASTE_ANY_MESSAGES_HERE + ``` + +**Any additional context?:** + +Is there anything else that might be useful for us to know? From d627a6bb12334e0622b77753e98ec20b28cffca9 Mon Sep 17 00:00:00 2001 From: Yasir Modak <42785357+ymodak@users.noreply.github.com> Date: Thu, 22 May 2025 12:47:35 -0700 Subject: [PATCH 4/8] Update support_request.md --- .github/ISSUE_TEMPLATE/support_request.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/support_request.md b/.github/ISSUE_TEMPLATE/support_request.md index ebde6c4..0119f92 100644 --- a/.github/ISSUE_TEMPLATE/support_request.md +++ b/.github/ISSUE_TEMPLATE/support_request.md @@ -12,7 +12,7 @@ Thanks for reaching out for help! To assist you efficiently, please provide as m **What do you need help with?** -Is this a question about how to do something, a configuration problem, or a general issue you can't solve?* +Is this a question about how to do something, a configuration problem, or a general issue you can't solve? **Describe the issue/question:** @@ -20,23 +20,23 @@ Clearly describe what you are trying to achieve, what problem you are facing, or **What have you tried so far? (Optional):** -List any steps you've already taken to troubleshoot, find information, or attempt a solution.* +List any steps you've already taken to troubleshoot, find information, or attempt a solution. **Expected outcome (Optional):** -If applicable, what did you hope would happen, or what solution are you looking for?* +If applicable, what did you hope would happen, or what solution are you looking for? **Screenshots/Videos (Optional):** -*If applicable, add screenshots or a short video that might help explain your situation.* +If applicable, add screenshots or a short video that might help explain your situation. **Environment & Details:** -Please provide details about your operating environment, relevant URLs, or any messages you see.* +Please provide details about your operating environment, relevant URLs, or any messages you see. -- **Operating System:** -- **Browser & Version (if applicable):** -- **Any relevant messages (e.g., from UI, console):** +- **Operating System:** +- **Browser & Version (if applicable):** +- **Any relevant messages (e.g., from UI, console):** ``` PASTE_ANY_MESSAGES_HERE ``` From 43f942555b627682c1e211b569357e9ab78a5c8a Mon Sep 17 00:00:00 2001 From: Yasir Modak <42785357+ymodak@users.noreply.github.com> Date: Thu, 22 May 2025 12:48:18 -0700 Subject: [PATCH 5/8] Update bug_report.md --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 2472859..a7d15c7 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,5 +1,5 @@ --- -name: Bug report +name: 🐛 Bug report about: Create a report to help us improve title: '' labels: '' From 707d5f0c81b725f2e39d4b05e5f784222877b6b1 Mon Sep 17 00:00:00 2001 From: Yasir Modak <42785357+ymodak@users.noreply.github.com> Date: Thu, 22 May 2025 12:49:30 -0700 Subject: [PATCH 6/8] Update feature_request.md --- .github/ISSUE_TEMPLATE/feature_request.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 2b7a665..ae26d6f 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,5 +1,5 @@ --- -name: Feature request +name: ✨ Feature request about: Suggest an idea for this project title: '' labels: '' From 61ee4fd5d78354b37ac9ea885c696fe4c1ce2af1 Mon Sep 17 00:00:00 2001 From: Jing Jin <8752427+jinjingforever@users.noreply.github.com> Date: Thu, 22 May 2025 16:37:23 -0700 Subject: [PATCH 7/8] - Fix some potential memory leak in image capture sheet. --- Android/src/app/build.gradle.kts | 2 +- .../ui/common/chat/MessageInputText.kt | 89 ++++++++----------- 2 files changed, 40 insertions(+), 51 deletions(-) diff --git a/Android/src/app/build.gradle.kts b/Android/src/app/build.gradle.kts index 9d63325..1a22a5c 100644 --- a/Android/src/app/build.gradle.kts +++ b/Android/src/app/build.gradle.kts @@ -31,7 +31,7 @@ android { minSdk = 26 targetSdk = 35 versionCode = 1 - versionName = "1.0.2" + versionName = "1.0.3" // Needed for HuggingFace auth workflows. manifestPlaceholders["appAuthRedirectScheme"] = "com.google.ai.edge.gallery.oauth" diff --git a/Android/src/app/src/main/java/com/google/ai/edge/gallery/ui/common/chat/MessageInputText.kt b/Android/src/app/src/main/java/com/google/ai/edge/gallery/ui/common/chat/MessageInputText.kt index 73b1713..824fc9c 100644 --- a/Android/src/app/src/main/java/com/google/ai/edge/gallery/ui/common/chat/MessageInputText.kt +++ b/Android/src/app/src/main/java/com/google/ai/edge/gallery/ui/common/chat/MessageInputText.kt @@ -23,6 +23,7 @@ import android.graphics.Bitmap import android.graphics.BitmapFactory import android.graphics.Matrix import android.net.Uri +import android.util.Log import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.PickVisualMediaRequest import androidx.activity.result.contract.ActivityResultContracts @@ -75,9 +76,11 @@ import androidx.compose.material3.TextField import androidx.compose.material3.TextFieldDefaults import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope @@ -104,6 +107,8 @@ import com.google.ai.edge.gallery.ui.theme.GalleryTheme import kotlinx.coroutines.launch import java.util.concurrent.Executors +private const val TAG = "AGMessageInputText" + /** * Composable function to display a text input field for composing chat messages. * @@ -135,7 +140,7 @@ fun MessageInputText( var showAddContentMenu by remember { mutableStateOf(false) } var showTextInputHistorySheet by remember { mutableStateOf(false) } var showCameraCaptureBottomSheet by remember { mutableStateOf(false) } - var cameraCaptureSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) + val cameraCaptureSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) var tempPhotoUri by remember { mutableStateOf(value = Uri.EMPTY) } var pickedImages by remember { mutableStateOf>(listOf()) } val updatePickedImages: (Bitmap) -> Unit = { bitmap -> @@ -150,21 +155,6 @@ fun MessageInputText( checkFrontCamera(context = context, callback = { hasFrontCamera = it }) } - // launches camera - val cameraLauncher = - rememberLauncherForActivityResult(ActivityResultContracts.TakePicture()) { isImageSaved -> - if (isImageSaved) { - handleImageSelected( - context = context, - uri = tempPhotoUri, - onImageSelected = { bitmap -> - updatePickedImages(bitmap) - }, - rotateForPortrait = true, - ) - } - } - // Permission request when taking picture. val takePicturePermissionLauncher = rememberLauncherForActivityResult( ActivityResultContracts.RequestPermission() @@ -173,7 +163,6 @@ fun MessageInputText( showAddContentMenu = false tempPhotoUri = context.createTempPictureUri() showCameraCaptureBottomSheet = true -// cameraLauncher.launch(tempPhotoUri) } } @@ -270,7 +259,6 @@ fun MessageInputText( showAddContentMenu = false tempPhotoUri = context.createTempPictureUri() showCameraCaptureBottomSheet = true -// cameraLauncher.launch(tempPhotoUri) } // Otherwise, ask for permission @@ -420,24 +408,26 @@ fun MessageInputText( var cameraProvider by remember { mutableStateOf(null) } var cameraControl by remember { mutableStateOf(null) } val localContext = LocalContext.current - var cameraSide by remember { mutableStateOf(CameraSelector.LENS_FACING_BACK) } - + var cameraSide by remember { mutableIntStateOf(CameraSelector.LENS_FACING_BACK) } val executor = remember { Executors.newSingleThreadExecutor() } - val capturedImageUri = remember { mutableStateOf(null) } fun rebindCameraProvider() { cameraProvider?.let { cameraProvider -> val cameraSelector = CameraSelector.Builder() .requireLensFacing(cameraSide) .build() - cameraProvider.unbindAll() - val camera = cameraProvider.bindToLifecycle( - lifecycleOwner = lifecycleOwner, - cameraSelector = cameraSelector, - previewUseCase, - imageCaptureUseCase - ) - cameraControl = camera.cameraControl + try { + cameraProvider.unbindAll() + val camera = cameraProvider.bindToLifecycle( + lifecycleOwner = lifecycleOwner, + cameraSelector = cameraSelector, + previewUseCase, + imageCaptureUseCase + ) + cameraControl = camera.cameraControl + } catch (e: Exception) { + Log.d(TAG, "Failed to bind camera", e) + } } } @@ -450,31 +440,25 @@ fun MessageInputText( rebindCameraProvider() } -// val cameraController = remember { -// LifecycleCameraController(context).apply { -// bindToLifecycle(lifecycleOwner) -// } -// } + DisposableEffect(Unit) { // Or key on lifecycleOwner if it makes more sense + onDispose { + cameraProvider?.unbindAll() // Unbind all use cases from the camera provider + if (!executor.isShutdown) { + executor.shutdown() // Shut down the executor service + } + } + } Box(modifier = Modifier.fillMaxSize()) { // PreviewView for the camera feed. AndroidView( modifier = Modifier.fillMaxSize(), factory = { ctx -> - PreviewView(context).also { + PreviewView(ctx).also { previewUseCase.surfaceProvider = it.surfaceProvider rebindCameraProvider() } -// PreviewView(ctx).apply { -// scaleType = PreviewView.ScaleType.FILL_START -// implementationMode = PreviewView.ImplementationMode.COMPATIBLE -// controller = cameraController // Attach the lifecycle-aware camera controller. -// } }, -// onRelease = { -// // Called when the PreviewView is removed from the composable hierarchy -// cameraController.unbind() // Unbinds the camera to free up resources -// } ) // Close button. @@ -508,9 +492,9 @@ fun MessageInputText( .size(64.dp) .border(2.dp, MaterialTheme.colorScheme.onPrimary, CircleShape), onClick = { - scope.launch { - val callback = object : ImageCapture.OnImageCapturedCallback() { - override fun onCaptureSuccess(image: ImageProxy) { + val callback = object : ImageCapture.OnImageCapturedCallback() { + override fun onCaptureSuccess(image: ImageProxy) { + try { var bitmap = image.toBitmap() val rotation = image.imageInfo.rotationDegrees bitmap = if (rotation != 0) { @@ -520,13 +504,18 @@ fun MessageInputText( Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true) } else bitmap updatePickedImages(bitmap) + } catch (e: Exception) { + Log.e(TAG, "Failed to process image", e) + } finally { image.close() + scope.launch { + cameraCaptureSheetState.hide() + showCameraCaptureBottomSheet = false + } } } - imageCaptureUseCase.takePicture(executor, callback) - cameraCaptureSheetState.hide() - showCameraCaptureBottomSheet = false } + imageCaptureUseCase.takePicture(executor, callback) }, ) { Icon( From ebb605131dc18145395526d2c0150c1656799a86 Mon Sep 17 00:00:00 2001 From: Jing Jin <8752427+jinjingforever@users.noreply.github.com> Date: Thu, 22 May 2025 16:51:22 -0700 Subject: [PATCH 8/8] - Try to limit the image size taken by the camera in ask image task --- .../ui/common/chat/MessageInputText.kt | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/Android/src/app/src/main/java/com/google/ai/edge/gallery/ui/common/chat/MessageInputText.kt b/Android/src/app/src/main/java/com/google/ai/edge/gallery/ui/common/chat/MessageInputText.kt index 824fc9c..49afffd 100644 --- a/Android/src/app/src/main/java/com/google/ai/edge/gallery/ui/common/chat/MessageInputText.kt +++ b/Android/src/app/src/main/java/com/google/ai/edge/gallery/ui/common/chat/MessageInputText.kt @@ -24,6 +24,7 @@ import android.graphics.BitmapFactory import android.graphics.Matrix import android.net.Uri import android.util.Log +import android.util.Size import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.PickVisualMediaRequest import androidx.activity.result.contract.ActivityResultContracts @@ -32,6 +33,9 @@ import androidx.camera.core.CameraControl import androidx.camera.core.CameraSelector import androidx.camera.core.ImageCapture import androidx.camera.core.ImageProxy +import androidx.camera.core.resolutionselector.AspectRatioStrategy +import androidx.camera.core.resolutionselector.ResolutionSelector +import androidx.camera.core.resolutionselector.ResolutionStrategy import androidx.camera.lifecycle.ProcessCameraProvider import androidx.camera.lifecycle.awaitInstance import androidx.camera.view.PreviewView @@ -404,7 +408,20 @@ fun MessageInputText( val lifecycleOwner = LocalLifecycleOwner.current val previewUseCase = remember { androidx.camera.core.Preview.Builder().build() } - val imageCaptureUseCase = remember { ImageCapture.Builder().build() } + val imageCaptureUseCase = remember { + // Try to limit the image size. + val preferredSize = Size(512, 512) + val resolutionStrategy = ResolutionStrategy( + preferredSize, + ResolutionStrategy.FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER + ) + val resolutionSelector = ResolutionSelector.Builder() + .setResolutionStrategy(resolutionStrategy) + .setAspectRatioStrategy(AspectRatioStrategy.RATIO_4_3_FALLBACK_AUTO_STRATEGY) + .build() + + ImageCapture.Builder().setResolutionSelector(resolutionSelector).build() + } var cameraProvider by remember { mutableStateOf(null) } var cameraControl by remember { mutableStateOf(null) } val localContext = LocalContext.current @@ -501,6 +518,7 @@ fun MessageInputText( val matrix = Matrix().apply { postRotate(rotation.toFloat()) } + Log.d(TAG, "image size: ${bitmap.width}, ${bitmap.height}") Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true) } else bitmap updatePickedImages(bitmap) @@ -590,7 +608,7 @@ private fun rotateImageIfNecessary(bitmap: Bitmap, rotateForPortrait: Boolean = private fun checkFrontCamera(context: Context, callback: (Boolean) -> Unit) { val cameraProviderFuture = ProcessCameraProvider.getInstance(context) - cameraProviderFuture.addListener(Runnable { + cameraProviderFuture.addListener({ val cameraProvider = cameraProviderFuture.get() try { // Attempt to select the default front camera