From c3bb460ae9ef4655ae8022587a7dbad68fbbca51 Mon Sep 17 00:00:00 2001 From: Kota-Jagadeesh Date: Tue, 14 Oct 2025 00:05:44 +0530 Subject: [PATCH 1/6] Enforce 5-image selection limit with warning dialog on click --- .../customselector/ui/adapter/ImageAdapter.kt | 76 ++++++++----------- 1 file changed, 31 insertions(+), 45 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapter.kt b/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapter.kt index c3ef4a784..8de605766 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapter.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapter.kt @@ -188,55 +188,38 @@ class ImageAdapter( defaultDispatcher, uploadingContributionList, ) - scope.launch { - val sharedPreferences: SharedPreferences = - context.getSharedPreferences(CUSTOM_SELECTOR_PREFERENCE_KEY, 0) - val showAlreadyActionedImages = - sharedPreferences.getBoolean(SHOW_ALREADY_ACTIONED_IMAGES_PREFERENCE_KEY, true) - if (!showAlreadyActionedImages) { - // If the position is not already visited, that means the position is new then - // finds the next actionable image position from all images - if (!alreadyAddedPositions.contains(position)) { - processThumbnailForActionedImage( - holder, - position, - uploadingContributionList - ) - _isLoadingImages.value = false - // If the position is already visited, that means the image is already present - // inside map, so it will fetch the image from the map and load in the holder - } else { - val actionableImages: List = ArrayList(actionableImagesMap.values) - if (actionableImages.size > position) { - image = actionableImages[position] - Glide - .with(holder.image) - .load(image.uri) - .thumbnail(0.3f) - .into(holder.image) - } + holder.itemView.setOnClickListener { + if (!holder.isItemUploaded() && !holder.isItemNotForUpload()) { + if (selectedImages.size >= 5 && !isSelected) { //enforce the 5-image limit + Toast.makeText(context, "Cannot select more than 5 images", Toast.LENGTH_SHORT).show() + return@setOnClickListener + } + if (isSelected) { + selectedImages.removeAt(selectedIndex) + holder.itemUnselected() + notifyItemChanged(position, ImageUnselected()) + imageSelectListener.onSelectedImagesChanged(selectedImages, selectedImages.size) + } else { + selectedImages.add(image) + holder.itemSelected() + notifyItemChanged(position, ImageSelectedOrUpdated()) + imageSelectListener.onSelectedImagesChanged(selectedImages, selectedImages.size) } - - // If switch is turned off, it just fetches the image from all images without any - // further operations - } else { - Glide - .with(holder.image) - .load(image.uri) - .thumbnail(0.3f) - .into(holder.image) } } - - holder.itemView.setOnClickListener { - onThumbnailClicked(position, holder) - } - - // launch media preview on long click. holder.itemView.setOnLongClickListener { - imageSelectListener.onLongPress(images.indexOf(image), images, selectedImages) + imageSelectListener.onLongPress(position, images, ArrayList(selectedImages)) true } + //handle close button click for deselection + holder.closeButton.setOnClickListener { + if (isSelected) { + selectedImages.removeAt(selectedIndex) + holder.itemUnselected() + notifyItemChanged(position, ImageUnselected()) + imageSelectListener.onSelectedImagesChanged(selectedImages, selectedImages.size) + } + } } } @@ -417,7 +400,7 @@ class ImageAdapter( * Set new selected images */ fun setSelectedImages(newSelectedImages: ArrayList) { - selectedImages = ArrayList(newSelectedImages) + selectedImages = ArrayList(newSelectedImages.take(5)) // enforce 5-image limit imageSelectListener.onSelectedImagesChanged(selectedImages, 0) } @@ -431,7 +414,7 @@ class ImageAdapter( ) { numberOfSelectedImagesMarkedAsNotForUpload = 0 images.clear() - selectedImages = arrayListOf() + selectedImages = ArrayList(selectedImages.take(5)) // enforce the 5-image limit init(newImages, fixedImages, TreeMap(), uploadingImages) notifyDataSetChanged() } @@ -523,12 +506,14 @@ class ImageAdapter( private val uploadingGroup: Group = itemView.findViewById(R.id.uploading_group) private val notForUploadGroup: Group = itemView.findViewById(R.id.not_for_upload_group) private val selectedGroup: Group = itemView.findViewById(R.id.selected_group) + val closeButton: ImageView = itemView.findViewById(R.id.close_button) // Added for close button /** * Item selected view. */ fun itemSelected() { selectedGroup.visibility = View.VISIBLE + closeButton.visibility = View.VISIBLE // Show close button when selected } /** @@ -536,6 +521,7 @@ class ImageAdapter( */ fun itemUnselected() { selectedGroup.visibility = View.GONE + closeButton.visibility = View.GONE // Hide close button when unselected } /** From 6edcab654ae2ca603f2177dc61a04c14ec126845 Mon Sep 17 00:00:00 2001 From: Kota-Jagadeesh Date: Tue, 14 Oct 2025 00:06:13 +0530 Subject: [PATCH 2/6] Set selection limit to 5 images and add warning for exceeding limit --- .../customselector/ui/selector/CustomSelectorActivity.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivity.kt b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivity.kt index 2534b4aeb..9fbba896d 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivity.kt @@ -107,7 +107,7 @@ class CustomSelectorActivity : /** * Maximum number of images that can be selected. */ - private var uploadLimit: Int = 20 + private var uploadLimit: Int = 5 // Changed to 5 asper the issue #3101 /** * Flag that is marked true when the amount @@ -650,8 +650,12 @@ class CustomSelectorActivity : finishPickImages(arrayListOf()) return } + if (selectedImages.size > uploadLimit) { + displayUploadLimitWarning() // shows the warning dialog if >5 images + return + } scope.launch(ioDispatcher) { - val uniqueImages = selectedImages.distinctBy { image -> + val uniqueImages = selectedImages.take(uploadLimit).distinctBy { image -> //enforce limit CustomSelectorUtils.getImageSHA1( image.uri, ioDispatcher, From d918b848e8ddf46d8e0b9e7ac1f6b7ef2376ae0b Mon Sep 17 00:00:00 2001 From: Kota-Jagadeesh Date: Tue, 14 Oct 2025 00:18:56 +0530 Subject: [PATCH 3/6] Enforced 5-image limit for selection and upload, added close button, updated warning dialog strings, and ensured thumbnail bar consistency --- .../free/nrw/commons/upload/ThumbnailsAdapter.kt | 2 +- .../fr/free/nrw/commons/upload/UploadActivity.kt | 7 +++++-- .../res/layout/item_custom_selector_image.xml | 15 ++++++++++++--- app/src/main/res/values/strings.xml | 2 ++ 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ThumbnailsAdapter.kt b/app/src/main/java/fr/free/nrw/commons/upload/ThumbnailsAdapter.kt index d467f9bf6..2cc8603be 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ThumbnailsAdapter.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/ThumbnailsAdapter.kt @@ -23,7 +23,7 @@ internal class ThumbnailsAdapter(private val callback: Callback) : var onThumbnailDeletedListener: OnThumbnailDeletedListener? = null var uploadableFiles: List = emptyList() set(value) { - field = value + field = value.take(5) //enforce 5-image limit notifyDataSetChanged() } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.kt b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.kt index c2bed5fff..4b2c72057 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.kt @@ -735,8 +735,8 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C intent.getParcelableArrayListExtra(EXTRA_FILES) } - // Convert to mutable list or return empty list if null - files?.toMutableList() ?: run { + // Convert to mutable list,takes up to 5 files, or return empty list if null + files?.toMutableList()?.take(5)?.toMutableList() ?: run { //enforce 5-image limit Timber.w("Files array was null") mutableListOf() } @@ -753,6 +753,9 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C Timber.d("File $index path: ${file.getFilePath()}") } + //update thumbnails adapter with limited files + thumbnailsAdapter?.uploadableFiles = uploadableFiles + // Handle other extras with null safety place = try { if (VERSION.SDK_INT >= VERSION_CODES.TIRAMISU) { diff --git a/app/src/main/res/layout/item_custom_selector_image.xml b/app/src/main/res/layout/item_custom_selector_image.xml index d7b8611d7..8b37389f7 100644 --- a/app/src/main/res/layout/item_custom_selector_image.xml +++ b/app/src/main/res/layout/item_custom_selector_image.xml @@ -1,4 +1,3 @@ - + - + app:constraint_referenced_ids="selected_overlay,selected_image,close_button"/> Created and uploaded by: %1$s Created by %1$s and uploaded by %2$s Nominated for Deletion + + You can only select up to 5 images. From 82cb5db14483808c9fe9cb4a2882d826b39357a6 Mon Sep 17 00:00:00 2001 From: Kota-Jagadeesh Date: Tue, 14 Oct 2025 18:05:03 +0530 Subject: [PATCH 4/6] Added 20-img limit --- .../nrw/commons/customselector/ui/adapter/ImageAdapter.kt | 6 +++--- .../customselector/ui/selector/CustomSelectorActivity.kt | 4 ++-- .../java/fr/free/nrw/commons/upload/ThumbnailsAdapter.kt | 2 +- .../main/java/fr/free/nrw/commons/upload/UploadActivity.kt | 4 ++-- app/src/main/res/values/strings.xml | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapter.kt b/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapter.kt index 8de605766..f96bcde10 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapter.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapter.kt @@ -190,8 +190,8 @@ class ImageAdapter( ) holder.itemView.setOnClickListener { if (!holder.isItemUploaded() && !holder.isItemNotForUpload()) { - if (selectedImages.size >= 5 && !isSelected) { //enforce the 5-image limit - Toast.makeText(context, "Cannot select more than 5 images", Toast.LENGTH_SHORT).show() + if (selectedImages.size >= 20 && !isSelected) { //enforce the 20-image limit + Toast.makeText(context, "Cannot select more than 20 images", Toast.LENGTH_SHORT).show() return@setOnClickListener } if (isSelected) { @@ -400,7 +400,7 @@ class ImageAdapter( * Set new selected images */ fun setSelectedImages(newSelectedImages: ArrayList) { - selectedImages = ArrayList(newSelectedImages.take(5)) // enforce 5-image limit + selectedImages = ArrayList(newSelectedImages.take(20)) // enforce 20-image limit imageSelectListener.onSelectedImagesChanged(selectedImages, 0) } diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivity.kt b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivity.kt index 9fbba896d..cc737063c 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivity.kt @@ -107,7 +107,7 @@ class CustomSelectorActivity : /** * Maximum number of images that can be selected. */ - private var uploadLimit: Int = 5 // Changed to 5 asper the issue #3101 + private var uploadLimit: Int = 20 // changed to 20 asper the issue #3101 /** * Flag that is marked true when the amount @@ -651,7 +651,7 @@ class CustomSelectorActivity : return } if (selectedImages.size > uploadLimit) { - displayUploadLimitWarning() // shows the warning dialog if >5 images + displayUploadLimitWarning() // shows the warning dialog if >20 images return } scope.launch(ioDispatcher) { diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ThumbnailsAdapter.kt b/app/src/main/java/fr/free/nrw/commons/upload/ThumbnailsAdapter.kt index 2cc8603be..b5156b722 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ThumbnailsAdapter.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/ThumbnailsAdapter.kt @@ -23,7 +23,7 @@ internal class ThumbnailsAdapter(private val callback: Callback) : var onThumbnailDeletedListener: OnThumbnailDeletedListener? = null var uploadableFiles: List = emptyList() set(value) { - field = value.take(5) //enforce 5-image limit + field = value.take(20) //enforce 20-image limit notifyDataSetChanged() } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.kt b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.kt index 4b2c72057..0984d646e 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.kt @@ -735,8 +735,8 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C intent.getParcelableArrayListExtra(EXTRA_FILES) } - // Convert to mutable list,takes up to 5 files, or return empty list if null - files?.toMutableList()?.take(5)?.toMutableList() ?: run { //enforce 5-image limit + // Convert to mutable list,takes up to 20 files, or return empty list if null + files?.toMutableList()?.take(20)?.toMutableList() ?: run { //enforce 20-image limit Timber.w("Files array was null") mutableListOf() } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 629b0dd79..c47e74a5a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -888,5 +888,5 @@ Upload your first media by tapping on the add button. Created by %1$s and uploaded by %2$s Nominated for Deletion - You can only select up to 5 images. + You can only select up to 20 images. From 38b10106f83459ee8adc3de62da4940644bd5d5e Mon Sep 17 00:00:00 2001 From: Kota-Jagadeesh Date: Mon, 20 Oct 2025 14:57:13 +0530 Subject: [PATCH 5/6] Fix:define the maximum number of images allowed for selection --- .../nrw/commons/customselector/helper/CustomSelectorConstants.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/helper/CustomSelectorConstants.kt b/app/src/main/java/fr/free/nrw/commons/customselector/helper/CustomSelectorConstants.kt index e03b4da3c..3df1e2265 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/helper/CustomSelectorConstants.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/helper/CustomSelectorConstants.kt @@ -10,4 +10,5 @@ object CustomSelectorConstants { const val NEW_SELECTED_IMAGES = "new_selected_images" const val SHOULD_REFRESH = "should_refresh" const val FULL_SCREEN_MODE_FIRST_LUNCH = "full_screen_mode_first_launch" + const val MAX_IMAGE_COUNT = 20 } From 24e6ac73187ff3ba37cde8ace5b4d7934465b987 Mon Sep 17 00:00:00 2001 From: Kota-Jagadeesh Date: Mon, 20 Oct 2025 14:59:12 +0530 Subject: [PATCH 6/6] Fix:initialize the adapter's limit from the constant --- .../nrw/commons/customselector/ui/adapter/ImageAdapter.kt | 5 +++-- .../customselector/ui/selector/CustomSelectorActivity.kt | 5 +++-- .../java/fr/free/nrw/commons/upload/ThumbnailsAdapter.kt | 3 ++- .../main/java/fr/free/nrw/commons/upload/UploadActivity.kt | 3 ++- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapter.kt b/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapter.kt index f96bcde10..3834b8016 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapter.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/adapter/ImageAdapter.kt @@ -16,6 +16,7 @@ import fr.free.nrw.commons.contributions.Contribution import fr.free.nrw.commons.customselector.helper.ImageHelper import fr.free.nrw.commons.customselector.helper.ImageHelper.CUSTOM_SELECTOR_PREFERENCE_KEY import fr.free.nrw.commons.customselector.helper.ImageHelper.SHOW_ALREADY_ACTIONED_IMAGES_PREFERENCE_KEY +import fr.free.nrw.commons.customselector.helper.CustomSelectorConstants.MAX_IMAGE_COUNT import fr.free.nrw.commons.customselector.listeners.ImageSelectListener import fr.free.nrw.commons.customselector.model.Image import fr.free.nrw.commons.customselector.ui.selector.ImageLoader @@ -190,7 +191,7 @@ class ImageAdapter( ) holder.itemView.setOnClickListener { if (!holder.isItemUploaded() && !holder.isItemNotForUpload()) { - if (selectedImages.size >= 20 && !isSelected) { //enforce the 20-image limit + if (selectedImages.size >= MAX_IMAGE_COUNT && !isSelected) { //enforce the 20-image limit Toast.makeText(context, "Cannot select more than 20 images", Toast.LENGTH_SHORT).show() return@setOnClickListener } @@ -400,7 +401,7 @@ class ImageAdapter( * Set new selected images */ fun setSelectedImages(newSelectedImages: ArrayList) { - selectedImages = ArrayList(newSelectedImages.take(20)) // enforce 20-image limit + selectedImages = ArrayList(newSelectedImages.take(MAX_IMAGE_COUNT)) // enforce 20-image limit imageSelectListener.onSelectedImagesChanged(selectedImages, 0) } diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivity.kt b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivity.kt index cc737063c..576604d5d 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/CustomSelectorActivity.kt @@ -47,6 +47,7 @@ import fr.free.nrw.commons.customselector.database.NotForUploadStatus import fr.free.nrw.commons.customselector.database.NotForUploadStatusDao import fr.free.nrw.commons.customselector.helper.CustomSelectorConstants import fr.free.nrw.commons.customselector.helper.FolderDeletionHelper +import fr.free.nrw.commons.customselector.helper.CustomSelectorConstants.MAX_IMAGE_COUNT import fr.free.nrw.commons.customselector.listeners.FolderClickListener import fr.free.nrw.commons.customselector.listeners.ImageSelectListener import fr.free.nrw.commons.customselector.model.Image @@ -107,7 +108,7 @@ class CustomSelectorActivity : /** * Maximum number of images that can be selected. */ - private var uploadLimit: Int = 20 // changed to 20 asper the issue #3101 + private var uploadLimit: Int = MAX_IMAGE_COUNT // changed to 20 asper the issue #3101 /** * Flag that is marked true when the amount @@ -214,7 +215,7 @@ class CustomSelectorActivity : ) // Check for single selection extra - uploadLimit = if (intent.getBooleanExtra(EXTRA_SINGLE_SELECTION, false)) 1 else 20 + uploadLimit = if (intent.getBooleanExtra(EXTRA_SINGLE_SELECTION, false)) 1 else MAX_IMAGE_COUNT setupViews() diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ThumbnailsAdapter.kt b/app/src/main/java/fr/free/nrw/commons/upload/ThumbnailsAdapter.kt index b5156b722..eda653afc 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/ThumbnailsAdapter.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/ThumbnailsAdapter.kt @@ -10,6 +10,7 @@ import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView import com.facebook.drawee.view.SimpleDraweeView import fr.free.nrw.commons.R +import fr.free.nrw.commons.customselector.helper.CustomSelectorConstants.MAX_IMAGE_COUNT import fr.free.nrw.commons.databinding.ItemUploadThumbnailBinding import fr.free.nrw.commons.filepicker.UploadableFile import java.io.File @@ -23,7 +24,7 @@ internal class ThumbnailsAdapter(private val callback: Callback) : var onThumbnailDeletedListener: OnThumbnailDeletedListener? = null var uploadableFiles: List = emptyList() set(value) { - field = value.take(20) //enforce 20-image limit + field = value.take(MAX_IMAGE_COUNT) //enforce 20-image limit notifyDataSetChanged() } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.kt b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.kt index 8c55ed556..1f1837232 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.kt @@ -27,6 +27,7 @@ import fr.free.nrw.commons.R import fr.free.nrw.commons.auth.LoginActivity import fr.free.nrw.commons.auth.SessionManager import fr.free.nrw.commons.contributions.ContributionController +import fr.free.nrw.commons.customselector.helper.CustomSelectorConstants.MAX_IMAGE_COUNT import fr.free.nrw.commons.databinding.ActivityUploadBinding import fr.free.nrw.commons.filepicker.Constants.RequestCodes import fr.free.nrw.commons.filepicker.UploadableFile @@ -735,7 +736,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C } // Convert to mutable list,takes up to 20 files, or return empty list if null - files?.toMutableList()?.take(20)?.toMutableList() ?: run { //enforce 20-image limit + files?.toMutableList()?.take(MAX_IMAGE_COUNT)?.toMutableList() ?: run { //enforce 20-image limit Timber.w("Files array was null") mutableListOf() }