From 2a9d5db51ed21355c052fbebfb425f41a105f9ba Mon Sep 17 00:00:00 2001 From: "Amir E. Aharoni" Date: Sat, 11 Oct 2025 08:33:54 -0400 Subject: [PATCH 01/21] Consistent spelling of "screenshots" in the issue template (#6481) "Screenshot" is written as one word without a hyphen everywhere else in this app's code, and generally in the English language. --- .github/ISSUE_TEMPLATE/bug-report.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml index a4682fd3c..dcbba0597 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yml +++ b/.github/ISSUE_TEMPLATE/bug-report.yml @@ -70,7 +70,7 @@ body: required: false - type: textarea attributes: - label: Screen-shots + label: Screenshots description: Add screenshots related to the issue (if available). Can be created by pressing the Volume Down and Power Button at the same time on Android 4.0 and higher. validations: required: false From 4c621364c9d881902a18ce60f3683fd173b302d5 Mon Sep 17 00:00:00 2001 From: VoidRaven Date: Sat, 11 Oct 2025 18:08:07 +0530 Subject: [PATCH 02/21] Fix/6404 app crashes theme change multi upload (#6429) * Prevent IndexOutOfBoundsException in setUploadMediaDetails by validating index and list size (#6404) * fixed UninitializedPropertyAccessException by safely initializing and accessing imageAdapter (#6404) * fixed indexOutOfBoundsException by safely handling saved state and index in onViewCreated (#6404) * resolve Unresolved reference by replacing setImageToBeUploaded with direct field assignments (#6404) * Fix test compilation by removing obsolete testSetImageToBeUploaded and adding tha testInitializeFragmentWithUploadItem (#6404) * Fix test compilation by removing testInitializeFragmentWithUploadItem with unresolved onImageProcessed (#6404) * fix: test failures in UploadMediaDetailFragmentUnitTest by removing obsolete tests and initializing defaultKvStore (#6404) * Fixed all the typos --------- Co-authored-by: Nicolas Raoul --- .../ui/selector/ImageFragment.kt | 73 ++++++++++-- .../free/nrw/commons/upload/UploadActivity.kt | 27 +++-- .../mediaDetails/UploadMediaDetailFragment.kt | 106 ++++++++++-------- .../mediaDetails/UploadMediaPresenter.kt | 13 ++- .../UploadMediaDetailFragmentUnitTest.kt | 50 ++------- 5 files changed, 154 insertions(+), 115 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/ImageFragment.kt b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/ImageFragment.kt index 4f37106cc..db250dea9 100644 --- a/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/ImageFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/customselector/ui/selector/ImageFragment.kt @@ -47,6 +47,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.launch +import timber.log.Timber import java.util.TreeMap import javax.inject.Inject import kotlin.collections.ArrayList @@ -211,8 +212,12 @@ class ImageFragment : savedInstanceState: Bundle?, ): View? { _binding = FragmentCustomSelectorBinding.inflate(inflater, container, false) - imageAdapter = - ImageAdapter(requireActivity(), activity as ImageSelectListener, imageLoader!!) + + // ensures imageAdapter is initialized + if (!::imageAdapter.isInitialized) { + imageAdapter = ImageAdapter(requireActivity(), activity as ImageSelectListener, imageLoader!!) + Timber.d("Initialized imageAdapter in onCreateView") + } // Set single selection mode if needed val singleSelection = (activity as? CustomSelectorActivity)?.intent?.getBooleanExtra(CustomSelectorActivity.EXTRA_SINGLE_SELECTION, false) == true imageAdapter.setSingleSelection(singleSelection) @@ -370,7 +375,12 @@ class ImageFragment : * notifyDataSetChanged, rebuild the holder views to account for deleted images. */ override fun onResume() { - imageAdapter.notifyDataSetChanged() + if (::imageAdapter.isInitialized) { + imageAdapter.notifyDataSetChanged() + Timber.d("Notified imageAdapter in onResume") + } else { + Timber.w("imageAdapter not initialized in onResume") + } super.onResume() } @@ -380,14 +390,19 @@ class ImageFragment : * Save the Image Fragment state. */ override fun onDestroy() { - imageAdapter.cleanUp() + if (::imageAdapter.isInitialized) { + imageAdapter.cleanUp() + Timber.d("Cleaned up imageAdapter in onDestroy") + } else { + Timber.w("imageAdapter not initialized in onDestroy, skipping cleanup") + } val position = - (selectorRV?.layoutManager as GridLayoutManager) - .findFirstVisibleItemPosition() + (selectorRV?.layoutManager as? GridLayoutManager) + ?.findFirstVisibleItemPosition() ?: -1 - // Check for empty RecyclerView. - if (position != -1 && filteredImages.size > 0) { + // check for valid position and non-empty image list + if (position != -1 && filteredImages.isNotEmpty() && ::imageAdapter.isInitialized) { context?.let { context -> context .getSharedPreferences( @@ -396,34 +411,57 @@ class ImageFragment : )?.let { prefs -> prefs.edit()?.let { editor -> editor.putLong("ItemId", imageAdapter.getImageIdAt(position))?.apply() + Timber.d("Saved last visible item ID: %d", imageAdapter.getImageIdAt(position)) } } } + } else { + Timber.d("Skipped saving item ID: position=%d, filteredImages.size=%d, imageAdapter initialized=%b", + position, filteredImages.size, ::imageAdapter.isInitialized) } super.onDestroy() } override fun onDestroyView() { _binding = null + selectorRV = null + loader = null + switch = null + progressLayout = null super.onDestroyView() } override fun refresh() { - imageAdapter.refresh(filteredImages, allImages, getUploadingContributions()) + if (::imageAdapter.isInitialized) { + imageAdapter.refresh(filteredImages, allImages, getUploadingContributions()) + Timber.d("Refreshed imageAdapter") + } else { + Timber.w("imageAdapter not initialized in refresh") + } } /** * Removes the image from the actionable image map */ fun removeImage(image: Image) { - imageAdapter.removeImageFromActionableImageMap(image) + if (::imageAdapter.isInitialized) { + imageAdapter.removeImageFromActionableImageMap(image) + Timber.d("Removed image from actionable image map") + } else { + Timber.w("imageAdapter not initialized in removeImage") + } } /** * Clears the selected images */ fun clearSelectedImages() { - imageAdapter.clearSelectedImages() + if (::imageAdapter.isInitialized) { + imageAdapter.clearSelectedImages() + Timber.d("Cleared selected images") + } else { + Timber.w("imageAdapter not initialized in clearSelectedImages") + } } /** @@ -434,6 +472,15 @@ class ImageFragment : selectedImages: ArrayList, shouldRefresh: Boolean, ) { + if (::imageAdapter.isInitialized) { + imageAdapter.setSelectedImages(selectedImages) + if (shouldRefresh) { + imageAdapter.refresh(filteredImages, allImages, getUploadingContributions()) + } + Timber.d("Passed %d selected images to imageAdapter, shouldRefresh=%b", selectedImages.size, shouldRefresh) + } else { + Timber.w("imageAdapter not initialized in passSelectedImages") + } } /** @@ -443,6 +490,7 @@ class ImageFragment : if (!progressDialog.isShowing) { progressDialogLayout.progressDialogText.text = text progressDialog.show() + Timber.d("Showing mark/unmark progress dialog: %s", text) } } @@ -452,6 +500,7 @@ class ImageFragment : fun dismissMarkUnmarkProgressDialog() { if (progressDialog.isShowing) { progressDialog.dismiss() + Timber.d("Dismissed mark/unmark progress dialog") } } @@ -461,4 +510,4 @@ class ImageFragment : listOf(Contribution.STATE_IN_PROGRESS, Contribution.STATE_FAILED, Contribution.STATE_QUEUED, Contribution.STATE_PAUSED), )?.subscribeOn(Schedulers.io()) ?.blockingGet() ?: emptyList() -} +} \ No newline at end of file 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..799d5b0f1 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 @@ -508,24 +508,17 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C fragments = mutableListOf() } - for (uploadableFile in uploadableFiles) { val uploadMediaDetailFragment = UploadMediaDetailFragment() - if (!uploadIsOfAPlace) { + // set fragment properties but defer initialization + uploadMediaDetailFragment.uploadableFile = uploadableFile + uploadMediaDetailFragment.place = place + uploadMediaDetailFragment.inAppPictureLocation = if (!uploadIsOfAPlace) { handleLocation() - uploadMediaDetailFragment.setImageToBeUploaded( - uploadableFile, - place, - currLocation - ) - locationManager!!.unregisterLocationManager() + currLocation } else { - uploadMediaDetailFragment.setImageToBeUploaded( - uploadableFile, - place, - currLocation - ) + currLocation } val uploadMediaDetailFragmentCallback: UploadMediaDetailFragmentCallback = @@ -580,13 +573,19 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C if (isFragmentsSaved) { val fragment = fragments!![0] as UploadMediaDetailFragment? fragment!!.fragmentCallback = uploadMediaDetailFragmentCallback + fragment.initializeFragment() } else { uploadMediaDetailFragment.fragmentCallback = uploadMediaDetailFragmentCallback fragments!!.add(uploadMediaDetailFragment) } } - //If fragments are not created, create them and add them to the fragments ArrayList + // unregister location manager after loop if needed + if (!uploadIsOfAPlace) { + locationManager!!.unregisterLocationManager() + } + + // If fragments are not created, create them and add them to the fragments ArrayList if (!isFragmentsSaved) { uploadCategoriesFragment = UploadCategoriesFragment() if (place != null) { diff --git a/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragment.kt b/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragment.kt index 6ece53170..acf55670e 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragment.kt @@ -119,8 +119,8 @@ class UploadMediaDetailFragment : UploadBaseFragment(), UploadMediaDetailsContra private var basicKvStore: BasicKvStore? = null private val keyForShowingAlertDialog = "isNoNetworkAlertDialogShowing" - private var uploadableFile: UploadableFile? = null - private var place: Place? = null + internal var uploadableFile: UploadableFile? = null + internal var place: Place? = null private lateinit var uploadMediaDetailAdapter: UploadMediaDetailAdapter var indexOfFragment = 0 var isExpanded = true @@ -142,19 +142,24 @@ class UploadMediaDetailFragment : UploadBaseFragment(), UploadMediaDetailsContra } } - fun setImageToBeUploaded( - uploadableFile: UploadableFile?, place: Place?, inAppPictureLocation: LatLng? - ) { - this.uploadableFile = uploadableFile - this.place = place - this.inAppPictureLocation = inAppPictureLocation - } - override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { _binding = FragmentUploadMediaDetailFragmentBinding.inflate(inflater, container, false) _binding!!.mediaDetailCardView.handleKeyboardInsets() + // intialise the adapter early to prevent uninitialized access + uploadMediaDetailAdapter = UploadMediaDetailAdapter( + this, + defaultKvStore.getString(Prefs.DESCRIPTION_LANGUAGE, "")!!, + recentLanguagesDao, voiceInputResultLauncher + ) + uploadMediaDetailAdapter.callback = + UploadMediaDetailAdapter.Callback { titleStringID: Int, messageStringId: Int -> + showInfoAlert(titleStringID, messageStringId) + } + uploadMediaDetailAdapter.eventListener = this + binding.rvDescriptions.layoutManager = LinearLayoutManager(context) + binding.rvDescriptions.adapter = uploadMediaDetailAdapter return binding.root } @@ -163,20 +168,48 @@ class UploadMediaDetailFragment : UploadBaseFragment(), UploadMediaDetailsContra basicKvStore = BasicKvStore(requireActivity(), "CurrentUploadImageQualities") - if (fragmentCallback != null) { - indexOfFragment = fragmentCallback!!.getIndexInViewFlipper(this) - initializeFragment() - } - + // restore adapter items from savedInstanceState if available if (savedInstanceState != null) { - if (uploadMediaDetailAdapter.items.isEmpty() && fragmentCallback != null) { - uploadMediaDetailAdapter.items = savedInstanceState.getParcelableArrayList(UPLOAD_MEDIA_DETAILS)!! - presenter.setUploadMediaDetails(uploadMediaDetailAdapter.items, indexOfFragment) + val savedItems = savedInstanceState.getParcelableArrayList(UPLOAD_MEDIA_DETAILS) + Timber.d("Restoring state: savedItems size = %s", savedItems?.size ?: "null") + if (savedItems != null && savedItems.isNotEmpty()) { + uploadMediaDetailAdapter.items = savedItems + // only call setUploadMediaDetails if indexOfFragment is valid + if (fragmentCallback != null) { + indexOfFragment = fragmentCallback!!.getIndexInViewFlipper(this) + if (indexOfFragment >= 0) { + presenter.setUploadMediaDetails(uploadMediaDetailAdapter.items, indexOfFragment) + Timber.d("Restored and set upload media details for index %d", indexOfFragment) + } else { + Timber.w("Invalid indexOfFragment %d, skipping setUploadMediaDetails", indexOfFragment) + } + } else { + Timber.w("fragmentCallback is null, skipping setUploadMediaDetails") + } + } else { + // initialize with a default UploadMediaDetail if saved state is empty or null + uploadMediaDetailAdapter.items = mutableListOf(UploadMediaDetail()) + Timber.d("Initialized default UploadMediaDetail due to empty or null savedItems") + } + } else { + // intitialise with a default UploadMediaDetail for fresh fragment + if (uploadMediaDetailAdapter.items.isEmpty()) { + uploadMediaDetailAdapter.items = mutableListOf(UploadMediaDetail()) + Timber.d("Initialized default UploadMediaDetail for new fragment") } } + if (fragmentCallback != null) { + indexOfFragment = fragmentCallback!!.getIndexInViewFlipper(this) + Timber.d("Fragment callback present, indexOfFragment = %d", indexOfFragment) + initializeFragment() + } else { + Timber.w("Fragment callback is null, skipping initializeFragment") + } + try { - if (!presenter.getImageQuality(indexOfFragment, inAppPictureLocation, requireActivity())) { + if (indexOfFragment >= 0 && !presenter.getImageQuality(indexOfFragment, inAppPictureLocation, requireActivity())) { + Timber.d("Image quality check failed, redirecting to MainActivity") startActivityWithFlags( requireActivity(), MainActivity::class.java, @@ -184,11 +217,12 @@ class UploadMediaDetailFragment : UploadBaseFragment(), UploadMediaDetailsContra Intent.FLAG_ACTIVITY_SINGLE_TOP ) } - } catch (_: Exception) { + } catch (e: Exception) { + Timber.e(e, "Error during image quality check") } } - private fun initializeFragment() { + internal fun initializeFragment() { if (_binding == null) { return } @@ -206,7 +240,6 @@ class UploadMediaDetailFragment : UploadBaseFragment(), UploadMediaDetailsContra presenter.setupBasicKvStoreFactory { BasicKvStore(requireActivity(), it) } presenter.receiveImage(uploadableFile, place, inAppPictureLocation) - initRecyclerView() with (binding){ if (indexOfFragment == 0) { @@ -265,24 +298,6 @@ class UploadMediaDetailFragment : UploadBaseFragment(), UploadMediaDetailsContra } } - /** - * init the description recycler veiw and caption recyclerview - */ - private fun initRecyclerView() { - uploadMediaDetailAdapter = UploadMediaDetailAdapter( - this, - defaultKvStore.getString(Prefs.DESCRIPTION_LANGUAGE, "")!!, - recentLanguagesDao, voiceInputResultLauncher - ) - uploadMediaDetailAdapter.callback = - UploadMediaDetailAdapter.Callback { titleStringID: Int, messageStringId: Int -> - showInfoAlert(titleStringID, messageStringId) - } - uploadMediaDetailAdapter.eventListener = this - binding.rvDescriptions.layoutManager = LinearLayoutManager(context) - binding.rvDescriptions.adapter = uploadMediaDetailAdapter - } - private fun showInfoAlert(titleStringID: Int, messageStringId: Int) { showAlertDialog( requireActivity(), @@ -590,16 +605,14 @@ class UploadMediaDetailFragment : UploadBaseFragment(), UploadMediaDetailsContra var defaultLongitude = -122.431297 var defaultZoom = 16.0 - val locationPickerIntent: Intent - /* Retrieve image location from EXIF if present or check if user has provided location while using the in-app camera. Use location of last UploadItem if none of them is available */ + val locationPickerIntent: Intent if (uploadItem.gpsCoords != null && uploadItem.gpsCoords!! .decLatitude != 0.0 && uploadItem.gpsCoords!!.decLongitude != 0.0 ) { - defaultLatitude = uploadItem.gpsCoords!! - .decLatitude + defaultLatitude = uploadItem.gpsCoords!!.decLatitude defaultLongitude = uploadItem.gpsCoords!!.decLongitude defaultZoom = uploadItem.gpsCoords!!.zoomLevel @@ -615,8 +628,7 @@ class UploadMediaDetailFragment : UploadBaseFragment(), UploadMediaDetailsContra defaultLongitude = locationLatLng[1].toDouble() } if (defaultKvStore.getString(LAST_ZOOM) != null) { - defaultZoom = defaultKvStore.getString(LAST_ZOOM)!! - .toDouble() + defaultZoom = defaultKvStore.getString(LAST_ZOOM)!!.toDouble() } locationPickerIntent = LocationPicker.IntentBuilder() @@ -907,4 +919,4 @@ class UploadMediaDetailFragment : UploadBaseFragment(), UploadMediaDetailsContra const val UPLOADABLE_FILE: String = "uploadable_file" const val UPLOAD_MEDIA_DETAILS: String = "upload_media_detail_adapter" } -} +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaPresenter.kt b/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaPresenter.kt index 4d565adb2..6e26e02a6 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaPresenter.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaPresenter.kt @@ -69,7 +69,18 @@ class UploadMediaPresenter @Inject constructor( uploadMediaDetails: List, uploadItemIndex: Int ) { - repository.getUploads()[uploadItemIndex].uploadMediaDetails = uploadMediaDetails.toMutableList() + val uploadItems = repository.getUploads() + if (uploadItemIndex >= 0 && uploadItemIndex < uploadItems.size) { + if (uploadMediaDetails.isNotEmpty()) { + uploadItems[uploadItemIndex].uploadMediaDetails = uploadMediaDetails.toMutableList() + Timber.d("Set uploadMediaDetails for index %d, size %d", uploadItemIndex, uploadMediaDetails.size) + } else { + uploadItems[uploadItemIndex].uploadMediaDetails = mutableListOf(UploadMediaDetail()) + Timber.w("Received empty uploadMediaDetails for index %d, initialized default", uploadItemIndex) + } + } else { + Timber.e("Invalid index %d for uploadItems size %d, skipping setUploadMediaDetails", uploadItemIndex, uploadItems.size) + } } override fun setupBasicKvStoreFactory(factory: (String) -> BasicKvStore) { diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragmentUnitTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragmentUnitTest.kt index a37bcc927..0cab47c67 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragmentUnitTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragmentUnitTest.kt @@ -145,6 +145,8 @@ class UploadMediaDetailFragmentUnitTest { ibExpandCollapse = view.findViewById(R.id.ib_expand_collapse) Whitebox.setInternalState(fragment, "uploadMediaDetailAdapter", uploadMediaDetailAdapter) + Whitebox.setInternalState(fragment, "defaultKvStore", defaultKvStore) + `when`(defaultKvStore.getString("description_language", "")).thenReturn("en") } @Test @@ -160,16 +162,10 @@ class UploadMediaDetailFragmentUnitTest { fragment.onCreate(Bundle()) } - @Test - @Throws(Exception::class) - fun testSetImageToBeUploaded() { - Shadows.shadowOf(Looper.getMainLooper()).idle() - fragment.setImageToBeUploaded(null, null, location) - } - @Test @Throws(Exception::class) fun testOnCreateView() { + Shadows.shadowOf(Looper.getMainLooper()).idle() fragment.onCreateView(layoutInflater, null, savedInstanceState) } @@ -181,34 +177,6 @@ class UploadMediaDetailFragmentUnitTest { fragment.onViewCreated(view, savedInstanceState) } - @Test - @Throws(Exception::class) - fun testInit() { - Shadows.shadowOf(Looper.getMainLooper()).idle() - Whitebox.setInternalState(fragment, "presenter", presenter) - val method: Method = - UploadMediaDetailFragment::class.java.getDeclaredMethod( - "initializeFragment", - ) - method.isAccessible = true - method.invoke(fragment) - } - - @Test - @Throws(Exception::class) - fun testInitCaseGetIndexInViewFlipperNonZero() { - Shadows.shadowOf(Looper.getMainLooper()).idle() - Whitebox.setInternalState(fragment, "presenter", presenter) - `when`(callback.getIndexInViewFlipper(fragment)).thenReturn(1) - `when`(callback.totalNumberOfSteps).thenReturn(5) - val method: Method = - UploadMediaDetailFragment::class.java.getDeclaredMethod( - "initializeFragment", - ) - method.isAccessible = true - method.invoke(fragment) - } - @Test @Throws(Exception::class) fun testShowInfoAlert() { @@ -317,7 +285,7 @@ class UploadMediaDetailFragmentUnitTest { @Test @Throws(Exception::class) fun testShowExternalMap() { - shadowOf(Looper.getMainLooper()).idle() + Shadows.shadowOf(Looper.getMainLooper()).idle() `when`(uploadItem.gpsCoords).thenReturn(imageCoordinates) `when`(imageCoordinates.decLatitude).thenReturn(0.0) `when`(imageCoordinates.decLongitude).thenReturn(0.0) @@ -328,7 +296,7 @@ class UploadMediaDetailFragmentUnitTest { @Test @Throws(Exception::class) fun testOnCameraPositionCallbackOnMapIconClicked() { - shadowOf(Looper.getMainLooper()).idle() + Shadows.shadowOf(Looper.getMainLooper()).idle() Mockito.mock(LocationPicker::class.java) val intent = Mockito.mock(Intent::class.java) val cameraPosition = Mockito.mock(CameraPosition::class.java) @@ -357,7 +325,7 @@ class UploadMediaDetailFragmentUnitTest { @Test @Throws(Exception::class) fun testOnCameraPositionCallbackAddLocationDialog() { - shadowOf(Looper.getMainLooper()).idle() + Shadows.shadowOf(Looper.getMainLooper()).idle() Mockito.mock(LocationPicker::class.java) val intent = Mockito.mock(Intent::class.java) val cameraPosition = Mockito.mock(CameraPosition::class.java) @@ -427,7 +395,7 @@ class UploadMediaDetailFragmentUnitTest { @Test @Throws(Exception::class) fun testRememberedZoomLevelOnNull() { - shadowOf(Looper.getMainLooper()).idle() + Shadows.shadowOf(Looper.getMainLooper()).idle() Whitebox.setInternalState(fragment, "defaultKvStore", defaultKvStore) `when`(uploadItem.gpsCoords).thenReturn(null) `when`(defaultKvStore.getString(LAST_ZOOM)).thenReturn("13.0") @@ -443,7 +411,7 @@ class UploadMediaDetailFragmentUnitTest { @Test @Throws(Exception::class) fun testRememberedZoomLevelOnNotNull() { - shadowOf(Looper.getMainLooper()).idle() + Shadows.shadowOf(Looper.getMainLooper()).idle() `when`(uploadItem.gpsCoords).thenReturn(imageCoordinates) `when`(imageCoordinates.decLatitude).thenReturn(8.0) `when`(imageCoordinates.decLongitude).thenReturn(-8.0) @@ -456,4 +424,4 @@ class UploadMediaDetailFragmentUnitTest { val shadowIntent: ShadowIntent = shadowOf(startedIntent) Assert.assertEquals(shadowIntent.intentClass, LocationPickerActivity::class.java) } -} +} \ No newline at end of file From 14d6c802414bde1156aa7c1fb261ff1644248ad4 Mon Sep 17 00:00:00 2001 From: Rohit Verma <101377978+rohit9625@users.noreply.github.com> Date: Sat, 11 Oct 2025 20:03:44 +0530 Subject: [PATCH 03/21] fix: remove location manager and update listener on pause (#6483) Co-authored-by: Nicolas Raoul --- .../java/fr/free/nrw/commons/explore/map/ExploreMapFragment.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.kt b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.kt index 0fc95dd17..cad6bd057 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.kt @@ -287,6 +287,8 @@ class ExploreMapFragment : CommonsDaggerSupportFragment(), ExploreMapContract.Vi super.onPause() // unregistering the broadcastReceiver, as it was causing an exception and a potential crash unregisterNetworkReceiver() + locationManager.unregisterLocationManager() + locationManager.removeLocationListener(this) } fun requestLocationIfNeeded() { From bb330c1771a41ffeabbde24cdcaf14c218c74e16 Mon Sep 17 00:00:00 2001 From: "Amir E. Aharoni" Date: Mon, 13 Oct 2025 04:57:27 -0400 Subject: [PATCH 04/21] Change "actioned" to "handled" in translatable strings (#6498) "actioned" is not so standard in English as a verb. "handled" sounds more appropriate. --- app/src/main/res/values/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0c0ae3a14..45e59cf3d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -788,8 +788,8 @@ Upload your first media by tapping on the add button. Unmark as not for upload Marking as not for upload Unmarking as not for upload - Show already actioned pictures - Hiding already actioned pictures + Show already handled pictures + Hiding already handled pictures No more images found This image is already uploaded Can not select this image for upload From d0965206cd0c9217ffa7352ed0c1995265b2462d Mon Sep 17 00:00:00 2001 From: "Amir E. Aharoni" Date: Mon, 13 Oct 2025 05:05:26 -0400 Subject: [PATCH 05/21] Cleanup whitespace in the custom_selector_info_text2 string (#6496) In the current state, it appears confusingly on translatewiki, with a space in the beginning of a line. This patch changes it to just two linebreaks. --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 45e59cf3d..f9270a976 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -736,7 +736,7 @@ Upload your first media by tapping on the add button. Back Welcome to Custom Picture Selector This picker shows you which pictures you have already uploaded to Commons. - Unlike the picture on the left, the picture on the right has the Commons logo indicating it is already uploaded. \n Touch and hold for image preview. + Unlike the picture on the left, the picture on the right has the Commons logo indicating it is already uploaded.\n\nTouch and hold for image preview. Awesome This image has already been uploaded to Commons. For technical reasons, the app can\'t reliably upload more than %1$d pictures at once. The upload limit of %1$d has been exceeded by %2$d. From fbd28a05649b542c02e74198b2136cd5e5394151 Mon Sep 17 00:00:00 2001 From: "Amir E. Aharoni" Date: Mon, 13 Oct 2025 05:09:43 -0400 Subject: [PATCH 06/21] Change capitalization of "Add Location" (#6493) This makes it consistent with "Edit Location" and "Edit Image", which are used in the same screen. --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f9270a976..a0a4ca260 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -767,7 +767,7 @@ Upload your first media by tapping on the add button. Location data helps Wiki editors find your picture, making it much more useful.\nYour recent uploads have no location.\nWe suggest you turn on location in your camera app\'s settings.\nThank you for uploading! No location found How about adding the place where this image was taken?\nLocation data helps Wiki editors find your picture, making it much more useful.\nThank you! - Add location + Add Location Please remove from this email any information that you are not comfortable sharing publicly. Also, please be aware that your email address with which you are posting, and the associated name and profile picture, will be visible publicly. Details Achievements are only available in the prod flavor. Please check the developer documentation. From 1e51c4c5d02c6086b38bc71bbc9697e01adcb355 Mon Sep 17 00:00:00 2001 From: "Amir E. Aharoni" Date: Mon, 13 Oct 2025 05:13:22 -0400 Subject: [PATCH 07/21] Remove the arrow next to "Add location" (#6491) This resolves #6489 using the "remove arrow" method. --- app/src/main/res/drawable/ic_arrow_16dp.xml | 4 ---- .../res/layout/fragment_upload_media_detail_fragment.xml | 7 ------- 2 files changed, 11 deletions(-) delete mode 100644 app/src/main/res/drawable/ic_arrow_16dp.xml diff --git a/app/src/main/res/drawable/ic_arrow_16dp.xml b/app/src/main/res/drawable/ic_arrow_16dp.xml deleted file mode 100644 index d99c0b5e4..000000000 --- a/app/src/main/res/drawable/ic_arrow_16dp.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - diff --git a/app/src/main/res/layout/fragment_upload_media_detail_fragment.xml b/app/src/main/res/layout/fragment_upload_media_detail_fragment.xml index cccf94c7b..e6f9e79e7 100644 --- a/app/src/main/res/layout/fragment_upload_media_detail_fragment.xml +++ b/app/src/main/res/layout/fragment_upload_media_detail_fragment.xml @@ -149,13 +149,6 @@ android:textColor="#2D8BA4" android:textSize="@dimen/normal_text" android:textStyle="bold" /> - - From 91ea4a6e7b3bba7cfd6bd1506d2614a31f97510c Mon Sep 17 00:00:00 2001 From: "Amir E. Aharoni" Date: Mon, 13 Oct 2025 05:43:59 -0400 Subject: [PATCH 08/21] Rephrase images_featured_explanation (#6484) Make the text of the panel consistent with its title. The title is "Featured images", so the text should use the same term. Also move this resource next to the title, to make it easier for the translators. --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a0a4ca260..183c683da 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -133,6 +133,7 @@ Settings Sign Up Featured Images + Featured images are contributions by highly skilled photographers and illustrators that the Wikimedia Commons community has chosen as some of the highest quality on the site. Custom Selector Category Peer Review @@ -529,7 +530,6 @@ Upload your first media by tapping on the add button. Error occurred while picking images Please wait… - Featured pictures are images from highly skilled photographers and illustrators that the Wikimedia Commons community has chosen as some of the highest quality on the site. Images Uploaded via Nearby places are the images which are uploaded by discovering places on the map. This feature allows editors to send a Thank you notification to users who make useful edits – by using a small thank link on the history page or diff page. Copy to the next items From b6014b017ca5a91fcee03fea7e785f3281d3d933 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Mon, 13 Oct 2025 14:03:10 +0200 Subject: [PATCH 09/21] Localisation updates from https://translatewiki.net. --- app/src/main/res/values-az/error.xml | 2 +- app/src/main/res/values-qq/error.xml | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-az/error.xml b/app/src/main/res/values-az/error.xml index 6e9503c43..53a971367 100644 --- a/app/src/main/res/values-az/error.xml +++ b/app/src/main/res/values-az/error.xml @@ -5,7 +5,7 @@ * Nemoralis --> - Nasazlıq + Commons çökdü Uups. Nəsə düzgün çalışmır! Nə etdiyinizi bizə deyin, sonra e-poçt vasitəsilə bizimlə paylaşın. Bu, bizə bunu düzəltməyə kömək edəcək! Təşəkkürlər! diff --git a/app/src/main/res/values-qq/error.xml b/app/src/main/res/values-qq/error.xml index e48ac7c9d..9fffe2e0f 100644 --- a/app/src/main/res/values-qq/error.xml +++ b/app/src/main/res/values-qq/error.xml @@ -1,9 +1,11 @@ Title of dialog to show when the app crashes + Shown when the application crashed. Prompt asking people to enter info about what they were doing when the app crashed Toast to be displayed once someone sends an error report thanking them.\n{{Identical|Thank you}} From 0c244f369c8b01889357994ac992a1819a240b56 Mon Sep 17 00:00:00 2001 From: "Amir E. Aharoni" Date: Mon, 13 Oct 2025 08:54:32 -0400 Subject: [PATCH 10/21] Replace android.R.string.* with R.string (#6499) All these messages are not really necessary because the app has its own localizations, and android.R.string sometimes doesn't display the localized string. Resolves #6470. --- .../nrw/commons/contributions/ContributionController.kt | 4 ++-- .../free/nrw/commons/description/DescriptionEditActivity.kt | 2 +- .../fr/free/nrw/commons/explore/map/ExploreMapFragment.kt | 4 ++-- .../explore/recentsearches/RecentSearchesFragment.kt | 6 +++--- .../free/nrw/commons/location/LocationPermissionsHelper.kt | 4 ++-- app/src/main/java/fr/free/nrw/commons/quiz/QuizChecker.kt | 2 +- .../java/fr/free/nrw/commons/quiz/QuizResultActivity.kt | 2 +- .../main/java/fr/free/nrw/commons/review/ReviewActivity.kt | 4 ++-- .../main/java/fr/free/nrw/commons/upload/UploadActivity.kt | 4 ++-- .../commons/upload/categories/UploadCategoriesFragment.kt | 2 +- .../fr/free/nrw/commons/upload/depicts/DepictsFragment.kt | 2 +- .../upload/mediaDetails/UploadMediaDetailFragment.kt | 4 ++-- .../main/java/fr/free/nrw/commons/utils/PermissionUtils.kt | 4 ++-- .../commons/description/DescriptionEditActivityUnitTest.kt | 4 ++-- 14 files changed, 24 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.kt b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.kt index 29267452b..b9532a12e 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.kt +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.kt @@ -180,8 +180,8 @@ class ContributionController @Inject constructor(@param:Named("default_preferenc showAlertDialog( activity, activity.getString(R.string.location_permission_title), activity.getString(R.string.in_app_camera_location_permission_rationale), - activity.getString(android.R.string.ok), - activity.getString(android.R.string.cancel), + activity.getString(R.string.ok), + activity.getString(R.string.cancel), { createDialogsAndHandleLocationPermissions( activity, diff --git a/app/src/main/java/fr/free/nrw/commons/description/DescriptionEditActivity.kt b/app/src/main/java/fr/free/nrw/commons/description/DescriptionEditActivity.kt index 89d43845b..b1f1b7f9b 100644 --- a/app/src/main/java/fr/free/nrw/commons/description/DescriptionEditActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/description/DescriptionEditActivity.kt @@ -150,7 +150,7 @@ class DescriptionEditActivity : this, getString(titleStringID), getString(messageStringId), - getString(android.R.string.ok), + getString(R.string.ok), null ) } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.kt b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.kt index cad6bd057..a1bae09fb 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.kt @@ -140,8 +140,8 @@ class ExploreMapFragment : CommonsDaggerSupportFragment(), ExploreMapContract.Vi requireActivity(), requireActivity().getString(R.string.location_permission_title), requireActivity().getString(R.string.location_permission_rationale_explore), - requireActivity().getString(android.R.string.ok), - requireActivity().getString(android.R.string.cancel), + requireActivity().getString(R.string.ok), + requireActivity().getString(R.string.cancel), { askForLocationPermission() }, null, null diff --git a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesFragment.kt b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesFragment.kt index c0f1bd5db..e7903c9ed 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/recentsearches/RecentSearchesFragment.kt @@ -67,10 +67,10 @@ class RecentSearchesFragment : CommonsDaggerSupportFragment() { private fun showDeleteRecentAlertDialog(context: Context) { AlertDialog.Builder(context) .setMessage(getString(R.string.delete_recent_searches_dialog)) - .setPositiveButton(android.R.string.yes) { dialog: DialogInterface, _: Int -> + .setPositiveButton(R.string.yes) { dialog: DialogInterface, _: Int -> setDeleteRecentPositiveButton(context, dialog) } - .setNegativeButton(android.R.string.no, null) + .setNegativeButton(R.string.no, null) .setCancelable(false) .create() .show() @@ -102,7 +102,7 @@ class RecentSearchesFragment : CommonsDaggerSupportFragment() { setDeletePositiveButton(context, dialog, position) } ) - .setNegativeButton(android.R.string.cancel, null) + .setNegativeButton(R.string.cancel, null) .setCancelable(false) .create() .show() diff --git a/app/src/main/java/fr/free/nrw/commons/location/LocationPermissionsHelper.kt b/app/src/main/java/fr/free/nrw/commons/location/LocationPermissionsHelper.kt index fefb59adb..47b4165ad 100644 --- a/app/src/main/java/fr/free/nrw/commons/location/LocationPermissionsHelper.kt +++ b/app/src/main/java/fr/free/nrw/commons/location/LocationPermissionsHelper.kt @@ -64,8 +64,8 @@ class LocationPermissionsHelper( activity, activity.getString(dialogTitleResource), activity.getString(dialogTextResource), - activity.getString(android.R.string.ok), - activity.getString(android.R.string.cancel), + activity.getString(R.string.ok), + activity.getString(R.string.cancel), { ActivityCompat.requestPermissions( activity, diff --git a/app/src/main/java/fr/free/nrw/commons/quiz/QuizChecker.kt b/app/src/main/java/fr/free/nrw/commons/quiz/QuizChecker.kt index 0183056a6..5362f1542 100644 --- a/app/src/main/java/fr/free/nrw/commons/quiz/QuizChecker.kt +++ b/app/src/main/java/fr/free/nrw/commons/quiz/QuizChecker.kt @@ -151,7 +151,7 @@ class QuizChecker @Inject constructor( activity.getString(R.string.quiz), activity.getString(R.string.quiz_alert_message, revertPercentageForMessage), activity.getString(R.string.about_translate_proceed), - activity.getString(android.R.string.cancel), + activity.getString(R.string.cancel), { startQuizActivity(activity) }, null ) diff --git a/app/src/main/java/fr/free/nrw/commons/quiz/QuizResultActivity.kt b/app/src/main/java/fr/free/nrw/commons/quiz/QuizResultActivity.kt index 6979edd15..15884146d 100644 --- a/app/src/main/java/fr/free/nrw/commons/quiz/QuizResultActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/quiz/QuizResultActivity.kt @@ -193,7 +193,7 @@ class QuizResultActivity : AppCompatActivity() { alertadd.setPositiveButton(R.string.about_translate_proceed) { dialog, _ -> shareScreen(screenshot) } - alertadd.setNegativeButton(android.R.string.cancel) { dialog, _ -> + alertadd.setNegativeButton(R.string.cancel) { dialog, _ -> dialog.cancel() } alertadd.show() diff --git a/app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.kt b/app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.kt index 1d26eacae..dccb77af1 100644 --- a/app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/review/ReviewActivity.kt @@ -238,7 +238,7 @@ class ReviewActivity : BaseActivity() { this, getString(R.string.skip_image).uppercase(Locale.ROOT), getString(R.string.skip_image_explanation), - getString(android.R.string.ok), + getString(R.string.ok), null, null, null @@ -250,7 +250,7 @@ class ReviewActivity : BaseActivity() { this, getString(R.string.title_activity_review), getString(R.string.review_image_explanation), - getString(android.R.string.ok), + getString(R.string.ok), null, null, null 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 799d5b0f1..66e0257f6 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 @@ -446,7 +446,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C this, getString(R.string.storage_permissions_denied), getString(R.string.unable_to_share_upload_item), - getString(android.R.string.ok) + getString(R.string.ok) ) { finish() } } else { showAlertDialog( @@ -455,7 +455,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C getString( R.string.write_storage_permission_rationale_for_image_share ), - getString(android.R.string.ok) + getString(R.string.ok) ) { checkStoragePermissions() } } } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/categories/UploadCategoriesFragment.kt b/app/src/main/java/fr/free/nrw/commons/upload/categories/UploadCategoriesFragment.kt index 798ab74fe..ee6af7bb6 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/categories/UploadCategoriesFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/categories/UploadCategoriesFragment.kt @@ -117,7 +117,7 @@ class UploadCategoriesFragment : UploadBaseFragment(), CategoriesContract.View { requireActivity(), getString(R.string.categories_activity_title), getString(R.string.categories_tooltip), - getString(android.R.string.ok), + getString(R.string.ok), null ) } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictsFragment.kt b/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictsFragment.kt index 484750410..5dcc2bf86 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictsFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/depicts/DepictsFragment.kt @@ -116,7 +116,7 @@ class DepictsFragment : UploadBaseFragment(), DepictsContract.View { requireActivity(), getString(R.string.depicts_step_title), getString(R.string.depicts_tooltip), - getString(android.R.string.ok), + getString(R.string.ok), null ) } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragment.kt b/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragment.kt index acf55670e..b3b067948 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailFragment.kt @@ -303,7 +303,7 @@ class UploadMediaDetailFragment : UploadBaseFragment(), UploadMediaDetailsContra requireActivity(), getString(titleStringID), getString(messageStringId), - getString(android.R.string.ok), + getString(R.string.ok), null ) } @@ -919,4 +919,4 @@ class UploadMediaDetailFragment : UploadBaseFragment(), UploadMediaDetailsContra const val UPLOADABLE_FILE: String = "uploadable_file" const val UPLOAD_MEDIA_DETAILS: String = "upload_media_detail_adapter" } -} \ No newline at end of file +} diff --git a/app/src/main/java/fr/free/nrw/commons/utils/PermissionUtils.kt b/app/src/main/java/fr/free/nrw/commons/utils/PermissionUtils.kt index df3b33bf6..5bcc9d1b2 100644 --- a/app/src/main/java/fr/free/nrw/commons/utils/PermissionUtils.kt +++ b/app/src/main/java/fr/free/nrw/commons/utils/PermissionUtils.kt @@ -209,8 +209,8 @@ object PermissionUtils { activity, activity.getString(rationaleTitle), activity.getString(rationaleMessage), - activity.getString(android.R.string.ok), - activity.getString(android.R.string.cancel), + activity.getString(R.string.ok), + activity.getString(R.string.cancel), { if (activity is UploadActivity) { activity.isShowPermissionsDialog = true diff --git a/app/src/test/kotlin/fr/free/nrw/commons/description/DescriptionEditActivityUnitTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/description/DescriptionEditActivityUnitTest.kt index be3b7e8e3..5b5dfd7dd 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/description/DescriptionEditActivityUnitTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/description/DescriptionEditActivityUnitTest.kt @@ -193,8 +193,8 @@ class DescriptionEditActivityUnitTest { method.isAccessible = true method.invoke( activity, - android.R.string.ok, - android.R.string.ok, + R.string.ok, + R.string.ok, ) val dialog: AlertDialog = ShadowAlertDialog.getLatestDialog() as AlertDialog assertEquals(dialog.isShowing, true) From a1c5974e936c369e099232b8930732d057491e72 Mon Sep 17 00:00:00 2001 From: "Amir E. Aharoni" Date: Tue, 14 Oct 2025 04:54:54 -0400 Subject: [PATCH 11/21] Fix depicts and categories pickers for RTL languages (#6503) This fixes the layouts to work in both left to right (LTR) and right to left (RTL) languages. Also replace two hard-coded strings in the depicts picker with proper string resources. Fixes #6502. --- .../res/layout/layout_upload_categories_item.xml | 12 +++++++++--- .../res/layout/layout_upload_depicts_item.xml | 16 +++++++++++----- .../res/layout/upload_categories_fragment.xml | 3 --- .../main/res/layout/upload_depicts_fragment.xml | 3 --- app/src/main/res/values-qq/strings.xml | 2 ++ app/src/main/res/values/strings.xml | 2 ++ 6 files changed, 24 insertions(+), 14 deletions(-) diff --git a/app/src/main/res/layout/layout_upload_categories_item.xml b/app/src/main/res/layout/layout_upload_categories_item.xml index 1c432ca88..146d55af3 100644 --- a/app/src/main/res/layout/layout_upload_categories_item.xml +++ b/app/src/main/res/layout/layout_upload_categories_item.xml @@ -3,6 +3,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/category_layout" + android:layoutDirection="locale" android:layout_width="match_parent" android:layout_height="wrap_content"> @@ -10,26 +11,31 @@ android:id="@+id/upload_category_checkbox" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:minWidth="48dp" + android:minHeight="48dp" android:checkMark="?android:attr/textCheckMark" android:checked="false" android:gravity="center_vertical" android:padding="@dimen/tiny_gap" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@+id/category_image" - app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> @@ -9,26 +10,31 @@ android:id="@+id/depict_checkbox" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:minWidth="48dp" + android:minHeight="48dp" android:checkMark="?android:attr/textCheckMark" android:checked="false" android:gravity="center_vertical" android:padding="@dimen/tiny_gap" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@+id/depicted_image" - app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> + android:text="@string/depicts_description" /> diff --git a/app/src/main/res/layout/upload_categories_fragment.xml b/app/src/main/res/layout/upload_categories_fragment.xml index 6ded4af87..cd053bce9 100644 --- a/app/src/main/res/layout/upload_categories_fragment.xml +++ b/app/src/main/res/layout/upload_categories_fragment.xml @@ -17,7 +17,6 @@ android:layout_width="wrap_content" android:layout_height="@dimen/half_standard_height" android:layout_marginEnd="@dimen/standard_gap" - android:layout_marginRight="@dimen/standard_gap" android:orientation="horizontal"> {{Optional}}\n<code>&amp;#169;</code> is the copyright symbol (©). {{Doc-commons-app-depicts}} {{Doc-commons-app-depicts}} + Label + Description Panning means moving the map left/right/up/down, typically by touching the screen with one finger and moving it.\n\nZooming means making the map\'s scale bigger or smaller, typically by pinching with two fingers.\n\nExample in other app:\nhttps://igss.schneider-electric.com/features/pan-and-zoom-in-definition/ A description of a visual element, location picker image shadow. Used for accesibility usually. {{Identical|Label}} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 183c683da..5ae4ca5e8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -710,6 +710,8 @@ Upload your first media by tapping on the add button. Please select the appropriate categories. Unlike depictions, categories are only in English. Commons makes your pictures reusable and adapted by everyone. Do you want to waive all rights? Do you want to be attributed? Do you want adaptations to use the same license? Depicts + Label + Description Media License Media Details View category page From c4cb65fc3c926c9ae854b8eb12e5ad52305c82ce Mon Sep 17 00:00:00 2001 From: "Amir E. Aharoni" Date: Tue, 14 Oct 2025 08:40:38 -0400 Subject: [PATCH 12/21] Improve the grammar of messages about GPX and KML files (#6497) Add articles, fix capitalization, add ellipses. --- app/src/main/res/values/strings.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5ae4ca5e8..05b3ee6f0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -827,12 +827,12 @@ Upload your first media by tapping on the add button. Your log-in has expired. Please log in again. No application available to open GPX files File Saved Successfully - Do you want to open GPX file? - Do you want to open KML file? - Failed to save KML file. - Failed to save GPX file. - Saving KML File - Saving GPX File + Do you want to open the GPX file? + Do you want to open the KML file? + Failed to save the KML file. + Failed to save the GPX file. + Saving as a KML file... + Saving as a GPX file... %d image selected %d images selected From 41d95814c94e2fe8e08dc9e4e2646e5981ef0dd1 Mon Sep 17 00:00:00 2001 From: "Amir E. Aharoni" Date: Tue, 14 Oct 2025 08:41:31 -0400 Subject: [PATCH 13/21] Remove the string SingleWebViewActivity (#6494) Resolves issue #6492. Remove the title of a web activity and the accompanying string resource. This was not a real translatable message, but something that looks more like an identifier that shouldn't be translated. As far as I can tell, it's not seen anywhere in the interface because the actual title is set in the code that calls it. --- app/src/main/AndroidManifest.xml | 3 +-- app/src/main/res/values/strings.xml | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e8215bd90..d83d645dd 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -57,8 +57,7 @@ tools:replace="android:appComponentFactory"> + android:exported="false" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 05b3ee6f0..d193b9aa2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -875,7 +875,6 @@ Upload your first media by tapping on the add button. Other wikis File usages - SingleWebViewActivity Account Vanish Account Vanish account warning From 61c9de6fcc444596c890880b64568beb4ab0a93a Mon Sep 17 00:00:00 2001 From: "Amir E. Aharoni" Date: Tue, 14 Oct 2025 08:42:09 -0400 Subject: [PATCH 14/21] Add a missing comma to a message (#6477) There should be a comma before "etc." in a list, and there already is a comma before "etc." in the string depicts_search_text_hint, so it should be in this string to for consistency. --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d193b9aa2..064b197e4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -619,7 +619,7 @@ Upload your first media by tapping on the add button. Exists Needs Photo Place type: - Bridge, museum, hotel etc. + Bridge, museum, hotel, etc. Something went wrong with log-in. You must reset your password! MEDIA CHILD CLASSES From 2e52adbef8b04a2b0c1187527b3271df849a263d Mon Sep 17 00:00:00 2001 From: "Amir E. Aharoni" Date: Tue, 14 Oct 2025 18:37:17 -0400 Subject: [PATCH 15/21] Clean up empty tags in XML files (#6505) This resolves all the "XML empty tags" lint errors. --- app/src/main/res/drawable/thumbnail_not_selected.xml | 2 +- app/src/main/res/layout/dialog_select_language.xml | 2 +- .../main/res/layout/fragment_upload_media_detail_fragment.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/res/drawable/thumbnail_not_selected.xml b/app/src/main/res/drawable/thumbnail_not_selected.xml index 8ead4b377..50404cf47 100644 --- a/app/src/main/res/drawable/thumbnail_not_selected.xml +++ b/app/src/main/res/drawable/thumbnail_not_selected.xml @@ -1,5 +1,5 @@ - + diff --git a/app/src/main/res/layout/dialog_select_language.xml b/app/src/main/res/layout/dialog_select_language.xml index 6fa70dcdb..dcef302fc 100644 --- a/app/src/main/res/layout/dialog_select_language.xml +++ b/app/src/main/res/layout/dialog_select_language.xml @@ -16,7 +16,7 @@ android:padding="12dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent"> + app:layout_constraintTop_toTopOf="parent" /> + android:orientation="horizontal" /> Date: Wed, 15 Oct 2025 00:52:05 -0400 Subject: [PATCH 16/21] Simplify android:gravity in two layouts (#6506) The "Inspect Code" linter complained that these two files had Right-to-left text compatibility issues. I couldn't really see any problems neither in English nor in Hebrew, but the linter's suggestion still made sense, so I cleaned it up. This fixes all the errors of the type "Android Lint: Internationalization / Right-to-left text compatibility issues". --- app/src/main/res/layout/fragment_review_image.xml | 6 ++---- app/src/main/res/layout/pic_of_day_app_widget.xml | 1 - 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/app/src/main/res/layout/fragment_review_image.xml b/app/src/main/res/layout/fragment_review_image.xml index c4e9e4f73..4a21f4df0 100644 --- a/app/src/main/res/layout/fragment_review_image.xml +++ b/app/src/main/res/layout/fragment_review_image.xml @@ -22,9 +22,8 @@ android:id="@+id/tv_review_question" android:layout_width="match_parent" android:layout_height="wrap_content" - android:gravity="center_vertical" + android:gravity="center" tools:text="testing1" - android:textAlignment="center" android:textColor="?attr/reviewHeading" android:textSize="32sp"/> @@ -33,9 +32,8 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="@dimen/filter_padding" - android:gravity="center_vertical" + android:gravity="center" tools:text="testing2" - android:textAlignment="center" android:textSize="22sp"/> diff --git a/app/src/main/res/layout/pic_of_day_app_widget.xml b/app/src/main/res/layout/pic_of_day_app_widget.xml index 46e00a0f4..beb3e20d4 100644 --- a/app/src/main/res/layout/pic_of_day_app_widget.xml +++ b/app/src/main/res/layout/pic_of_day_app_widget.xml @@ -35,7 +35,6 @@ android:layout_height="wrap_content" android:id="@+id/appwidget_title" android:textAlignment="center" - android:layout_gravity="bottom" android:textColor="@color/white" android:layout_marginTop="@dimen/filter_padding" android:layout_marginStart="@dimen/tiny_padding" From b48905a153b39a3408e0c3ff8fb162da4678f41f Mon Sep 17 00:00:00 2001 From: "Amir E. Aharoni" Date: Thu, 16 Oct 2025 06:19:02 -0400 Subject: [PATCH 17/21] Change all parameters to numbered parameters (#6516) This will solve these errors: "Format string is not a valid format string so it should not be passed to String.format" --- app/src/main/res/values/strings.xml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 064b197e4..f368df38c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -34,12 +34,12 @@ Starting Uploads - Processing %d upload - Processing %d uploads + Processing %1$d upload + Processing %1$d uploads - %d upload - %d uploads + %1$d upload + %1$d uploads This image will be licensed under %1$s @@ -83,7 +83,7 @@ Upload queued (limited connection mode enabled) %1$s uploaded! Tap to view your upload - Uploading file: %s + Uploading file: %1$s %1$s uploading Finishing uploading %1$s Failed to upload %1$s @@ -388,9 +388,9 @@ Thanks Received Featured Images Images via \"Nearby Places\" - Level %d - %s (Level %s) - %s (%s) + Level %1$d + %1$s (Level %2$s) + %1$s (%2$s) Images Uploaded Images Not Reverted Images Used @@ -610,7 +610,7 @@ Upload your first media by tapping on the add button. Share image via You haven\'t made any contributions yet - %s has not made any contributions yet + %1$s has not made any contributions yet Account created! Text copied to clipboard Notification marked as read @@ -757,8 +757,8 @@ Upload your first media by tapping on the add button. Please turn on location services to view nearby places. Location access is needed to show nearby places on the map. - Contributions of User: %s - Achievements of User: %s + Contributions of User: %1$s + Achievements of User: %1$s View user profile Edit depictions Edit categories @@ -834,8 +834,8 @@ Upload your first media by tapping on the add button. Saving as a KML file... Saving as a GPX file... - %d image selected - %d images selected + %1$d image selected + %1$d images selected Please remember that all images in a multi-upload get the same categories and depictions. If the images do not share depictions and categories, please perform several separate uploads. Note about multi-uploads From 1ff2a28326b3a29a809c360b8a23994320135ce3 Mon Sep 17 00:00:00 2001 From: "Amir E. Aharoni" Date: Thu, 16 Oct 2025 06:19:30 -0400 Subject: [PATCH 18/21] Replace tab with space in an XML layout file (#6514) I was working on this file recently, and Android Studio showed a warning that it has tabs instead of spaces, so here's it's fixed. A minor thing, but prevents distractions. --- app/src/main/res/layout/pic_of_day_app_widget.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/layout/pic_of_day_app_widget.xml b/app/src/main/res/layout/pic_of_day_app_widget.xml index beb3e20d4..53e54a7cf 100644 --- a/app/src/main/res/layout/pic_of_day_app_widget.xml +++ b/app/src/main/res/layout/pic_of_day_app_widget.xml @@ -46,6 +46,6 @@ android:id="@+id/appwidget_image" android:layout_width="match_parent" android:layout_height="wrap_content" - android:contentDescription="@string/appwidget_img" /> + android:contentDescription="@string/appwidget_img" /> From 1ecaf09f21ff215f7dd2f5d00e50602ad0ec07b0 Mon Sep 17 00:00:00 2001 From: "Amir E. Aharoni" Date: Thu, 16 Oct 2025 06:20:18 -0400 Subject: [PATCH 19/21] Remove wikimedia_licenses.xml and files that use it (#6513) This file doesn't seem to be used. Resolves #6504. --- app/src/main/res/xml/wikimedia_licenses.xml | 18 ------ update-license-info/Makefile | 14 ---- update-license-info/include-stubs.php | 68 -------------------- update-license-info/licenses.php | 71 --------------------- 4 files changed, 171 deletions(-) delete mode 100644 app/src/main/res/xml/wikimedia_licenses.xml delete mode 100644 update-license-info/Makefile delete mode 100644 update-license-info/include-stubs.php delete mode 100644 update-license-info/licenses.php diff --git a/app/src/main/res/xml/wikimedia_licenses.xml b/app/src/main/res/xml/wikimedia_licenses.xml deleted file mode 100644 index b9ac19124..000000000 --- a/app/src/main/res/xml/wikimedia_licenses.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/update-license-info/Makefile b/update-license-info/Makefile deleted file mode 100644 index a6c96ee2a..000000000 --- a/update-license-info/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -.FAKE : build update clean install - -build : ../app/src/main/res/xml/wikimedia_licenses.xml - -../app/src/main/res/xml/wikimedia_licenses.xml : licenses.php mediawiki-extensions-UploadWizard - php licenses.php > ../app/src/main/res/xml/wikimedia_licenses.xml - -mediawiki-extensions-UploadWizard : update - -update : - if [ -d mediawiki-extensions-UploadWizard ]; then (cd mediawiki-extensions-UploadWizard && git pull origin master); else git clone https://gerrit.wikimedia.org/r/mediawiki/extensions/UploadWizard mediawiki-extensions-UploadWizard; fi - -clean : - rm -rf mediawiki-extensions-UploadWizard diff --git a/update-license-info/include-stubs.php b/update-license-info/include-stubs.php deleted file mode 100644 index c0a01a0d6..000000000 --- a/update-license-info/include-stubs.php +++ /dev/null @@ -1,68 +0,0 @@ - 'English' ); - } -} -$wgMemc = new FakeMemc(); - -class FakeMessage { - function plain() { - return 'stub-message-plain'; - } - function parse() { - return 'stub-message-parsed'; - } -} - -function wfMessage() { - return new FakeMessage(); -} - -/** - * Converts shorthand byte notation to integer form - * - * @param $string String - * @return Integer - */ -function wfShorthandToInteger( $string = '' ) { - $string = trim( $string ); - if ( $string === '' ) { - return -1; - } - $last = $string[strlen( $string ) - 1]; - $val = intval( $string ); - switch ( $last ) { - case 'g': - case 'G': - $val *= 1024; - // break intentionally missing - case 'm': - case 'M': - $val *= 1024; - // break intentionally missing - case 'k': - case 'K': - $val *= 1024; - } - - return $val; -} - -$wgAPIModules = array(); diff --git a/update-license-info/licenses.php b/update-license-info/licenses.php deleted file mode 100644 index badda1a08..000000000 --- a/update-license-info/licenses.php +++ /dev/null @@ -1,71 +0,0 @@ - -// 2013-09-30 - -require 'include-stubs.php'; -$config = require "mediawiki-extensions-UploadWizard/UploadWizard.config.php"; -require "mediawiki-extensions-UploadWizard/UploadWizard.i18n.php"; -$licenseList = array(); - -foreach ( $config['licenses'] as $key => $license ) { - // Determine template -> license mappings - if ( isset( $license['templates'] ) ) { - $templates = $license['templates']; - } else { - $templates = array( $key ); - } - - if ( count( $templates ) < 1 ) { - throw new Exception("No templates for $key, this is wrong."); - } - if ( count( $templates ) > 1 ) { - //echo "Skipping multi-template license: $key\n"; - continue; - } - $template = $templates[0]; - if ( preg_match( '/^subst:/i', $template ) ) { - //echo "Skipping subst license: $key\n"; - continue; - } - - $msg = $messages['en'][$license['msg']]; - - $licenseInfo = array( - 'desc' => $msg, - 'template' => $template - ); - if ( isset( $license['url'] ) ) { - $url = $license['url']; - if ( substr( $url, 0, 2 ) == '//' ) { - $url = 'https:' . $url; - } - if ( isset( $license['languageCodePrefix'] ) ) { - $url .= $license['languageCodePrefix'] . '$lang'; - } - $licenseInfo['url'] = $url; - } - $licenseList[$key] = $licenseInfo; -} - -//var_dump( $licenseList ); - -echo "\n"; -echo "\n"; -foreach( $licenseList as $key => $licenseInfo ) { - $encId = htmlspecialchars( $key ); - echo " \n"; - -} -echo "\n"; - \ No newline at end of file From 864884e7b2379169b053cf7aa361590cfa6b76d0 Mon Sep 17 00:00:00 2001 From: "Amir E. Aharoni" Date: Thu, 16 Oct 2025 06:22:02 -0400 Subject: [PATCH 20/21] Update alternative texts for the welcome screen (#6512) * Update alternative texts for the welcome screen I've also updated their documentation for translators (qq) in transltaewiki itself. Resolves #689. * Fixed typo --------- Co-authored-by: Nicolas Raoul --- app/src/main/res/values/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f368df38c..996d0edba 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -233,8 +233,8 @@ Llamas Rainbow Bridge Tulip - Welcome Wikipedia - Welcome Copyright + A schematic image of a generic Wikipedia article. + A drawing of a stack of papers with the copyright symbol crossed out. Sydney Opera House Cancel Open From 7a865df9098fa2b38277262a3925f2dec7bb7212 Mon Sep 17 00:00:00 2001 From: Xinyu Yang Date: Thu, 16 Oct 2025 21:34:17 +1100 Subject: [PATCH 21/21] fix the bug of map reset (#6509) Co-authored-by: Chengxu Yang Co-authored-by: Nicolas Raoul --- ...NearbyFilterSearchRecyclerViewAdapter.java | 3 +++ .../nearby/fragments/NearbyParentFragment.kt | 25 ++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyFilterSearchRecyclerViewAdapter.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyFilterSearchRecyclerViewAdapter.java index b5f760c9f..53e9970a6 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyFilterSearchRecyclerViewAdapter.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyFilterSearchRecyclerViewAdapter.java @@ -91,6 +91,7 @@ public class NearbyFilterSearchRecyclerViewAdapter label.setSelected(!label.isSelected()); holder.placeTypeLayout.setSelected(label.isSelected()); + NearbyFilterState.setSelectedLabels(new ArrayList<>(selectedLabels)); callback.filterByMarkerType(selectedLabels, 0, false, false); }); } @@ -152,6 +153,7 @@ public class NearbyFilterSearchRecyclerViewAdapter label.setSelected(false); selectedLabels.remove(label); } + NearbyFilterState.setSelectedLabels(new ArrayList<>(selectedLabels)); notifyDataSetChanged(); } @@ -163,6 +165,7 @@ public class NearbyFilterSearchRecyclerViewAdapter selectedLabels.add(label); } } + NearbyFilterState.setSelectedLabels(new ArrayList<>(selectedLabels)); notifyDataSetChanged(); } diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.kt b/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.kt index 5c991f465..3e6e71511 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.kt @@ -881,6 +881,12 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(), fun initNearbyFilter() { binding!!.nearbyFilterList.root.visibility = View.GONE hideBottomSheet() + binding!!.nearbyFilter.searchViewLayout.searchView.apply { + setIconifiedByDefault(false) + isIconified = false + setQuery("", false) + clearFocus() + } binding!!.nearbyFilter.searchViewLayout.searchView.setOnQueryTextFocusChangeListener { v, hasFocus -> setLayoutHeightAlignedToWidth( 1.25, @@ -924,6 +930,7 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(), return _isDarkTheme } }) + restoreStoredFilterSelection() binding!!.nearbyFilterList.root .layoutParams.width = getScreenWidth( requireActivity(), @@ -942,6 +949,22 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(), }) } + private fun restoreStoredFilterSelection() { + val adapter = nearbyFilterSearchRecyclerViewAdapter ?: return + val savedLabels = ArrayList(NearbyFilterState.getInstance().selectedLabels) + adapter.selectedLabels.clear() + val savedSet = savedLabels.toSet() + Label.valuesAsList().forEach { label -> + val isSelected = savedSet.contains(label) + label.setSelected(isSelected) + if (isSelected) { + adapter.selectedLabels.add(label) + } + } + NearbyFilterState.setSelectedLabels(ArrayList(adapter.selectedLabels)) + adapter.notifyDataSetChanged() + } + override fun setCheckBoxAction() { binding!!.nearbyFilterList.checkboxTriStates.addAction() binding!!.nearbyFilterList.checkboxTriStates.state = CheckBoxTriStates.UNKNOWN @@ -2987,4 +3010,4 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(), return input.contains("(") || input.contains(")") } } -} \ No newline at end of file +}