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
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/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/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/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 82ec6a540..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
@@ -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() {
@@ -963,13 +965,17 @@ class ExploreMapFragment : CommonsDaggerSupportFragment(), ExploreMapContract.Vi
if (geoPoint != null) {
binding!!.mapView.controller.setCenter(geoPoint)
val overlays = binding!!.mapView.overlays
+ // collects the indices of items to remove
+ val indicesToRemove = mutableListOf()
for (i in overlays.indices) {
- if (overlays[i] is Marker) {
- binding!!.mapView.overlays.removeAt(i)
- } else if (overlays[i] is ScaleDiskOverlay) {
- binding!!.mapView.overlays.removeAt(i)
+ if (overlays[i] is Marker || overlays[i] is ScaleDiskOverlay) {
+ indicesToRemove.add(i)
}
}
+ // removes the items in reverse order to avoid index shifting
+ indicesToRemove.sortedDescending().forEach { index ->
+ binding!!.mapView.overlays.removeAt(index)
+ }
val diskOverlay = ScaleDiskOverlay(
requireContext(),
geoPoint, 2000, GeoConstants.UnitOfMeasure.foot
@@ -979,7 +985,6 @@ class ExploreMapFragment : CommonsDaggerSupportFragment(), ExploreMapContract.Vi
this.style = Paint.Style.STROKE
this.strokeWidth = 2f
})
-
setCirclePaint1(Paint().apply {
setColor(Color.argb(40, 128, 128, 128))
this.style = Paint.Style.FILL_AND_STROKE
@@ -988,7 +993,6 @@ class ExploreMapFragment : CommonsDaggerSupportFragment(), ExploreMapContract.Vi
setDisplaySizeMax(1700)
}
binding!!.mapView.overlays.add(diskOverlay)
-
val startMarker = Marker(
binding!!.mapView
).apply {
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/media/MediaDetailFragment.kt b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.kt
index dc1e86137..41e65ae4e 100644
--- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.kt
+++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.kt
@@ -1027,12 +1027,12 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C
val message: String = if (result) {
context.getString(
R.string.send_thank_success_message,
- media!!.displayTitle
+ media!!.user
)
} else {
context.getString(
R.string.send_thank_failure_message,
- media!!.displayTitle
+ media!!.user
)
}
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 c2bed5fff..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() }
}
}
@@ -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/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 6ece53170..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
@@ -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,30 +298,12 @@ 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(),
getString(titleStringID),
getString(messageStringId),
- getString(android.R.string.ok),
+ getString(R.string.ok),
null
)
}
@@ -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()
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/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/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" />
-
-
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">
- 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}}
diff --git a/app/src/main/res/values-qq/strings.xml b/app/src/main/res/values-qq/strings.xml
index bc4869a83..1eb19052f 100644
--- a/app/src/main/res/values-qq/strings.xml
+++ b/app/src/main/res/values-qq/strings.xml
@@ -468,6 +468,8 @@
{{Optional}}\n<code>&#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 45519a370..064b197e4 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
@@ -493,8 +494,8 @@ Upload your first media by tapping on the add button.
Requesting category check for %1$s
Done
Sending Thanks: Success
- Successfully sent thanks to %1$s
- Failed to send thanks %1$s
+ Sent thanks to %1$s
+ Failed to send thanks to %1$s
Sending Thanks: Failure
Sending Thanks for %1$s
@@ -529,10 +530,9 @@ 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 subsequent media
+ Copy to the next items
Copied
Examples of good images to upload to Commons
Examples of images not to upload
@@ -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
@@ -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
@@ -736,7 +738,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.
@@ -767,7 +769,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.
@@ -788,8 +790,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
@@ -825,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
@@ -873,7 +875,6 @@ Upload your first media by tapping on the add button.
Other wikis
•
File usages
- SingleWebViewActivity
Account
Vanish Account
Vanish account warning
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)
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