Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -211,8 +212,12 @@ class ImageFragment :
savedInstanceState: Bundle?,
): View? {
_binding = FragmentCustomSelectorBinding.inflate(inflater, container, false)
imageAdapter =
ImageAdapter(requireActivity(), activity as ImageSelectListener, imageLoader!!)

// ensuress 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)
Expand Down Expand Up @@ -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()
}

Expand All @@ -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) {
// cheeck for valid position and non-empty image list
if (position != -1 && filteredImages.isNotEmpty() && ::imageAdapter.isInitialized) {
context?.let { context ->
context
.getSharedPreferences(
Expand All @@ -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")
}
}

/**
Expand All @@ -434,6 +472,15 @@ class ImageFragment :
selectedImages: ArrayList<Image>,
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")
}
}

/**
Expand All @@ -443,6 +490,7 @@ class ImageFragment :
if (!progressDialog.isShowing) {
progressDialogLayout.progressDialogText.text = text
progressDialog.show()
Timber.d("Showing mark/unmark progress dialog: %s", text)
}
}

Expand All @@ -452,6 +500,7 @@ class ImageFragment :
fun dismissMarkUnmarkProgressDialog() {
if (progressDialog.isShowing) {
progressDialog.dismiss()
Timber.d("Dismissed mark/unmark progress dialog")
}
}

Expand All @@ -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()
}
}
25 changes: 12 additions & 13 deletions app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down Expand Up @@ -580,12 +573,18 @@ 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)
}
}

// unregisteer 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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}

Expand All @@ -163,32 +168,61 @@ class UploadMediaDetailFragment : UploadBaseFragment(), UploadMediaDetailsContra

basicKvStore = BasicKvStore(requireActivity(), "CurrentUploadImageQualities")

// restoree adapter items from savedInstanceState if available
if (savedInstanceState != null) {
val savedItems = savedInstanceState.getParcelableArrayList<UploadMediaDetail>(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()
}

if (savedInstanceState != null) {
if (uploadMediaDetailAdapter.items.isEmpty() && fragmentCallback != null) {
uploadMediaDetailAdapter.items = savedInstanceState.getParcelableArrayList(UPLOAD_MEDIA_DETAILS)!!
presenter.setUploadMediaDetails(uploadMediaDetailAdapter.items, indexOfFragment)
}
} 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,
Intent.FLAG_ACTIVITY_CLEAR_TOP,
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
}
Expand All @@ -206,7 +240,6 @@ class UploadMediaDetailFragment : UploadBaseFragment(), UploadMediaDetailsContra
presenter.setupBasicKvStoreFactory { BasicKvStore(requireActivity(), it) }

presenter.receiveImage(uploadableFile, place, inAppPictureLocation)
initRecyclerView()

with (binding){
if (indexOfFragment == 0) {
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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

Expand All @@ -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()
Expand Down Expand Up @@ -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"
}
}
}
Loading
Loading