mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 12:23:58 +01:00
Compare commits
3 commits
b8d340fbe8
...
14d6c80241
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
14d6c80241 | ||
|
|
4c621364c9 | ||
|
|
2a9d5db51e |
7 changed files with 157 additions and 116 deletions
2
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
|
|
@ -70,7 +70,7 @@ body:
|
||||||
required: false
|
required: false
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
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.
|
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:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import timber.log.Timber
|
||||||
import java.util.TreeMap
|
import java.util.TreeMap
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
|
|
@ -211,8 +212,12 @@ class ImageFragment :
|
||||||
savedInstanceState: Bundle?,
|
savedInstanceState: Bundle?,
|
||||||
): View? {
|
): View? {
|
||||||
_binding = FragmentCustomSelectorBinding.inflate(inflater, container, false)
|
_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
|
// Set single selection mode if needed
|
||||||
val singleSelection = (activity as? CustomSelectorActivity)?.intent?.getBooleanExtra(CustomSelectorActivity.EXTRA_SINGLE_SELECTION, false) == true
|
val singleSelection = (activity as? CustomSelectorActivity)?.intent?.getBooleanExtra(CustomSelectorActivity.EXTRA_SINGLE_SELECTION, false) == true
|
||||||
imageAdapter.setSingleSelection(singleSelection)
|
imageAdapter.setSingleSelection(singleSelection)
|
||||||
|
|
@ -370,7 +375,12 @@ class ImageFragment :
|
||||||
* notifyDataSetChanged, rebuild the holder views to account for deleted images.
|
* notifyDataSetChanged, rebuild the holder views to account for deleted images.
|
||||||
*/
|
*/
|
||||||
override fun onResume() {
|
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()
|
super.onResume()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -380,14 +390,19 @@ class ImageFragment :
|
||||||
* Save the Image Fragment state.
|
* Save the Image Fragment state.
|
||||||
*/
|
*/
|
||||||
override fun onDestroy() {
|
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 =
|
val position =
|
||||||
(selectorRV?.layoutManager as GridLayoutManager)
|
(selectorRV?.layoutManager as? GridLayoutManager)
|
||||||
.findFirstVisibleItemPosition()
|
?.findFirstVisibleItemPosition() ?: -1
|
||||||
|
|
||||||
// Check for empty RecyclerView.
|
// check for valid position and non-empty image list
|
||||||
if (position != -1 && filteredImages.size > 0) {
|
if (position != -1 && filteredImages.isNotEmpty() && ::imageAdapter.isInitialized) {
|
||||||
context?.let { context ->
|
context?.let { context ->
|
||||||
context
|
context
|
||||||
.getSharedPreferences(
|
.getSharedPreferences(
|
||||||
|
|
@ -396,34 +411,57 @@ class ImageFragment :
|
||||||
)?.let { prefs ->
|
)?.let { prefs ->
|
||||||
prefs.edit()?.let { editor ->
|
prefs.edit()?.let { editor ->
|
||||||
editor.putLong("ItemId", imageAdapter.getImageIdAt(position))?.apply()
|
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()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
_binding = null
|
_binding = null
|
||||||
|
selectorRV = null
|
||||||
|
loader = null
|
||||||
|
switch = null
|
||||||
|
progressLayout = null
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun refresh() {
|
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
|
* Removes the image from the actionable image map
|
||||||
*/
|
*/
|
||||||
fun removeImage(image: Image) {
|
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
|
* Clears the selected images
|
||||||
*/
|
*/
|
||||||
fun clearSelectedImages() {
|
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<Image>,
|
selectedImages: ArrayList<Image>,
|
||||||
shouldRefresh: Boolean,
|
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) {
|
if (!progressDialog.isShowing) {
|
||||||
progressDialogLayout.progressDialogText.text = text
|
progressDialogLayout.progressDialogText.text = text
|
||||||
progressDialog.show()
|
progressDialog.show()
|
||||||
|
Timber.d("Showing mark/unmark progress dialog: %s", text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -452,6 +500,7 @@ class ImageFragment :
|
||||||
fun dismissMarkUnmarkProgressDialog() {
|
fun dismissMarkUnmarkProgressDialog() {
|
||||||
if (progressDialog.isShowing) {
|
if (progressDialog.isShowing) {
|
||||||
progressDialog.dismiss()
|
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),
|
listOf(Contribution.STATE_IN_PROGRESS, Contribution.STATE_FAILED, Contribution.STATE_QUEUED, Contribution.STATE_PAUSED),
|
||||||
)?.subscribeOn(Schedulers.io())
|
)?.subscribeOn(Schedulers.io())
|
||||||
?.blockingGet() ?: emptyList()
|
?.blockingGet() ?: emptyList()
|
||||||
}
|
}
|
||||||
|
|
@ -287,6 +287,8 @@ class ExploreMapFragment : CommonsDaggerSupportFragment(), ExploreMapContract.Vi
|
||||||
super.onPause()
|
super.onPause()
|
||||||
// unregistering the broadcastReceiver, as it was causing an exception and a potential crash
|
// unregistering the broadcastReceiver, as it was causing an exception and a potential crash
|
||||||
unregisterNetworkReceiver()
|
unregisterNetworkReceiver()
|
||||||
|
locationManager.unregisterLocationManager()
|
||||||
|
locationManager.removeLocationListener(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun requestLocationIfNeeded() {
|
fun requestLocationIfNeeded() {
|
||||||
|
|
|
||||||
|
|
@ -508,24 +508,17 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C
|
||||||
fragments = mutableListOf()
|
fragments = mutableListOf()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
for (uploadableFile in uploadableFiles) {
|
for (uploadableFile in uploadableFiles) {
|
||||||
val uploadMediaDetailFragment = UploadMediaDetailFragment()
|
val uploadMediaDetailFragment = UploadMediaDetailFragment()
|
||||||
|
|
||||||
if (!uploadIsOfAPlace) {
|
// set fragment properties but defer initialization
|
||||||
|
uploadMediaDetailFragment.uploadableFile = uploadableFile
|
||||||
|
uploadMediaDetailFragment.place = place
|
||||||
|
uploadMediaDetailFragment.inAppPictureLocation = if (!uploadIsOfAPlace) {
|
||||||
handleLocation()
|
handleLocation()
|
||||||
uploadMediaDetailFragment.setImageToBeUploaded(
|
currLocation
|
||||||
uploadableFile,
|
|
||||||
place,
|
|
||||||
currLocation
|
|
||||||
)
|
|
||||||
locationManager!!.unregisterLocationManager()
|
|
||||||
} else {
|
} else {
|
||||||
uploadMediaDetailFragment.setImageToBeUploaded(
|
currLocation
|
||||||
uploadableFile,
|
|
||||||
place,
|
|
||||||
currLocation
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val uploadMediaDetailFragmentCallback: UploadMediaDetailFragmentCallback =
|
val uploadMediaDetailFragmentCallback: UploadMediaDetailFragmentCallback =
|
||||||
|
|
@ -580,13 +573,19 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C
|
||||||
if (isFragmentsSaved) {
|
if (isFragmentsSaved) {
|
||||||
val fragment = fragments!![0] as UploadMediaDetailFragment?
|
val fragment = fragments!![0] as UploadMediaDetailFragment?
|
||||||
fragment!!.fragmentCallback = uploadMediaDetailFragmentCallback
|
fragment!!.fragmentCallback = uploadMediaDetailFragmentCallback
|
||||||
|
fragment.initializeFragment()
|
||||||
} else {
|
} else {
|
||||||
uploadMediaDetailFragment.fragmentCallback = uploadMediaDetailFragmentCallback
|
uploadMediaDetailFragment.fragmentCallback = uploadMediaDetailFragmentCallback
|
||||||
fragments!!.add(uploadMediaDetailFragment)
|
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) {
|
if (!isFragmentsSaved) {
|
||||||
uploadCategoriesFragment = UploadCategoriesFragment()
|
uploadCategoriesFragment = UploadCategoriesFragment()
|
||||||
if (place != null) {
|
if (place != null) {
|
||||||
|
|
|
||||||
|
|
@ -119,8 +119,8 @@ class UploadMediaDetailFragment : UploadBaseFragment(), UploadMediaDetailsContra
|
||||||
|
|
||||||
private var basicKvStore: BasicKvStore? = null
|
private var basicKvStore: BasicKvStore? = null
|
||||||
private val keyForShowingAlertDialog = "isNoNetworkAlertDialogShowing"
|
private val keyForShowingAlertDialog = "isNoNetworkAlertDialogShowing"
|
||||||
private var uploadableFile: UploadableFile? = null
|
internal var uploadableFile: UploadableFile? = null
|
||||||
private var place: Place? = null
|
internal var place: Place? = null
|
||||||
private lateinit var uploadMediaDetailAdapter: UploadMediaDetailAdapter
|
private lateinit var uploadMediaDetailAdapter: UploadMediaDetailAdapter
|
||||||
var indexOfFragment = 0
|
var indexOfFragment = 0
|
||||||
var isExpanded = true
|
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(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
|
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
|
||||||
): View {
|
): View {
|
||||||
_binding = FragmentUploadMediaDetailFragmentBinding.inflate(inflater, container, false)
|
_binding = FragmentUploadMediaDetailFragmentBinding.inflate(inflater, container, false)
|
||||||
_binding!!.mediaDetailCardView.handleKeyboardInsets()
|
_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
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -163,20 +168,48 @@ class UploadMediaDetailFragment : UploadBaseFragment(), UploadMediaDetailsContra
|
||||||
|
|
||||||
basicKvStore = BasicKvStore(requireActivity(), "CurrentUploadImageQualities")
|
basicKvStore = BasicKvStore(requireActivity(), "CurrentUploadImageQualities")
|
||||||
|
|
||||||
if (fragmentCallback != null) {
|
// restore adapter items from savedInstanceState if available
|
||||||
indexOfFragment = fragmentCallback!!.getIndexInViewFlipper(this)
|
|
||||||
initializeFragment()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (savedInstanceState != null) {
|
if (savedInstanceState != null) {
|
||||||
if (uploadMediaDetailAdapter.items.isEmpty() && fragmentCallback != null) {
|
val savedItems = savedInstanceState.getParcelableArrayList<UploadMediaDetail>(UPLOAD_MEDIA_DETAILS)
|
||||||
uploadMediaDetailAdapter.items = savedInstanceState.getParcelableArrayList(UPLOAD_MEDIA_DETAILS)!!
|
Timber.d("Restoring state: savedItems size = %s", savedItems?.size ?: "null")
|
||||||
presenter.setUploadMediaDetails(uploadMediaDetailAdapter.items, indexOfFragment)
|
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 {
|
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(
|
startActivityWithFlags(
|
||||||
requireActivity(),
|
requireActivity(),
|
||||||
MainActivity::class.java,
|
MainActivity::class.java,
|
||||||
|
|
@ -184,11 +217,12 @@ class UploadMediaDetailFragment : UploadBaseFragment(), UploadMediaDetailsContra
|
||||||
Intent.FLAG_ACTIVITY_SINGLE_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) {
|
if (_binding == null) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -206,7 +240,6 @@ class UploadMediaDetailFragment : UploadBaseFragment(), UploadMediaDetailsContra
|
||||||
presenter.setupBasicKvStoreFactory { BasicKvStore(requireActivity(), it) }
|
presenter.setupBasicKvStoreFactory { BasicKvStore(requireActivity(), it) }
|
||||||
|
|
||||||
presenter.receiveImage(uploadableFile, place, inAppPictureLocation)
|
presenter.receiveImage(uploadableFile, place, inAppPictureLocation)
|
||||||
initRecyclerView()
|
|
||||||
|
|
||||||
with (binding){
|
with (binding){
|
||||||
if (indexOfFragment == 0) {
|
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) {
|
private fun showInfoAlert(titleStringID: Int, messageStringId: Int) {
|
||||||
showAlertDialog(
|
showAlertDialog(
|
||||||
requireActivity(),
|
requireActivity(),
|
||||||
|
|
@ -590,16 +605,14 @@ class UploadMediaDetailFragment : UploadBaseFragment(), UploadMediaDetailsContra
|
||||||
var defaultLongitude = -122.431297
|
var defaultLongitude = -122.431297
|
||||||
var defaultZoom = 16.0
|
var defaultZoom = 16.0
|
||||||
|
|
||||||
val locationPickerIntent: Intent
|
|
||||||
|
|
||||||
/* Retrieve image location from EXIF if present or
|
/* Retrieve image location from EXIF if present or
|
||||||
check if user has provided location while using the in-app camera.
|
check if user has provided location while using the in-app camera.
|
||||||
Use location of last UploadItem if none of them is available */
|
Use location of last UploadItem if none of them is available */
|
||||||
|
val locationPickerIntent: Intent
|
||||||
if (uploadItem.gpsCoords != null && uploadItem.gpsCoords!!
|
if (uploadItem.gpsCoords != null && uploadItem.gpsCoords!!
|
||||||
.decLatitude != 0.0 && uploadItem.gpsCoords!!.decLongitude != 0.0
|
.decLatitude != 0.0 && uploadItem.gpsCoords!!.decLongitude != 0.0
|
||||||
) {
|
) {
|
||||||
defaultLatitude = uploadItem.gpsCoords!!
|
defaultLatitude = uploadItem.gpsCoords!!.decLatitude
|
||||||
.decLatitude
|
|
||||||
defaultLongitude = uploadItem.gpsCoords!!.decLongitude
|
defaultLongitude = uploadItem.gpsCoords!!.decLongitude
|
||||||
defaultZoom = uploadItem.gpsCoords!!.zoomLevel
|
defaultZoom = uploadItem.gpsCoords!!.zoomLevel
|
||||||
|
|
||||||
|
|
@ -615,8 +628,7 @@ class UploadMediaDetailFragment : UploadBaseFragment(), UploadMediaDetailsContra
|
||||||
defaultLongitude = locationLatLng[1].toDouble()
|
defaultLongitude = locationLatLng[1].toDouble()
|
||||||
}
|
}
|
||||||
if (defaultKvStore.getString(LAST_ZOOM) != null) {
|
if (defaultKvStore.getString(LAST_ZOOM) != null) {
|
||||||
defaultZoom = defaultKvStore.getString(LAST_ZOOM)!!
|
defaultZoom = defaultKvStore.getString(LAST_ZOOM)!!.toDouble()
|
||||||
.toDouble()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
locationPickerIntent = LocationPicker.IntentBuilder()
|
locationPickerIntent = LocationPicker.IntentBuilder()
|
||||||
|
|
@ -907,4 +919,4 @@ class UploadMediaDetailFragment : UploadBaseFragment(), UploadMediaDetailsContra
|
||||||
const val UPLOADABLE_FILE: String = "uploadable_file"
|
const val UPLOADABLE_FILE: String = "uploadable_file"
|
||||||
const val UPLOAD_MEDIA_DETAILS: String = "upload_media_detail_adapter"
|
const val UPLOAD_MEDIA_DETAILS: String = "upload_media_detail_adapter"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -69,7 +69,18 @@ class UploadMediaPresenter @Inject constructor(
|
||||||
uploadMediaDetails: List<UploadMediaDetail>,
|
uploadMediaDetails: List<UploadMediaDetail>,
|
||||||
uploadItemIndex: Int
|
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) {
|
override fun setupBasicKvStoreFactory(factory: (String) -> BasicKvStore) {
|
||||||
|
|
|
||||||
|
|
@ -145,6 +145,8 @@ class UploadMediaDetailFragmentUnitTest {
|
||||||
ibExpandCollapse = view.findViewById(R.id.ib_expand_collapse)
|
ibExpandCollapse = view.findViewById(R.id.ib_expand_collapse)
|
||||||
|
|
||||||
Whitebox.setInternalState(fragment, "uploadMediaDetailAdapter", uploadMediaDetailAdapter)
|
Whitebox.setInternalState(fragment, "uploadMediaDetailAdapter", uploadMediaDetailAdapter)
|
||||||
|
Whitebox.setInternalState(fragment, "defaultKvStore", defaultKvStore)
|
||||||
|
`when`(defaultKvStore.getString("description_language", "")).thenReturn("en")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -160,16 +162,10 @@ class UploadMediaDetailFragmentUnitTest {
|
||||||
fragment.onCreate(Bundle())
|
fragment.onCreate(Bundle())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
@Throws(Exception::class)
|
|
||||||
fun testSetImageToBeUploaded() {
|
|
||||||
Shadows.shadowOf(Looper.getMainLooper()).idle()
|
|
||||||
fragment.setImageToBeUploaded(null, null, location)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
fun testOnCreateView() {
|
fun testOnCreateView() {
|
||||||
|
Shadows.shadowOf(Looper.getMainLooper()).idle()
|
||||||
fragment.onCreateView(layoutInflater, null, savedInstanceState)
|
fragment.onCreateView(layoutInflater, null, savedInstanceState)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -181,34 +177,6 @@ class UploadMediaDetailFragmentUnitTest {
|
||||||
fragment.onViewCreated(view, savedInstanceState)
|
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
|
@Test
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
fun testShowInfoAlert() {
|
fun testShowInfoAlert() {
|
||||||
|
|
@ -317,7 +285,7 @@ class UploadMediaDetailFragmentUnitTest {
|
||||||
@Test
|
@Test
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
fun testShowExternalMap() {
|
fun testShowExternalMap() {
|
||||||
shadowOf(Looper.getMainLooper()).idle()
|
Shadows.shadowOf(Looper.getMainLooper()).idle()
|
||||||
`when`(uploadItem.gpsCoords).thenReturn(imageCoordinates)
|
`when`(uploadItem.gpsCoords).thenReturn(imageCoordinates)
|
||||||
`when`(imageCoordinates.decLatitude).thenReturn(0.0)
|
`when`(imageCoordinates.decLatitude).thenReturn(0.0)
|
||||||
`when`(imageCoordinates.decLongitude).thenReturn(0.0)
|
`when`(imageCoordinates.decLongitude).thenReturn(0.0)
|
||||||
|
|
@ -328,7 +296,7 @@ class UploadMediaDetailFragmentUnitTest {
|
||||||
@Test
|
@Test
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
fun testOnCameraPositionCallbackOnMapIconClicked() {
|
fun testOnCameraPositionCallbackOnMapIconClicked() {
|
||||||
shadowOf(Looper.getMainLooper()).idle()
|
Shadows.shadowOf(Looper.getMainLooper()).idle()
|
||||||
Mockito.mock(LocationPicker::class.java)
|
Mockito.mock(LocationPicker::class.java)
|
||||||
val intent = Mockito.mock(Intent::class.java)
|
val intent = Mockito.mock(Intent::class.java)
|
||||||
val cameraPosition = Mockito.mock(CameraPosition::class.java)
|
val cameraPosition = Mockito.mock(CameraPosition::class.java)
|
||||||
|
|
@ -357,7 +325,7 @@ class UploadMediaDetailFragmentUnitTest {
|
||||||
@Test
|
@Test
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
fun testOnCameraPositionCallbackAddLocationDialog() {
|
fun testOnCameraPositionCallbackAddLocationDialog() {
|
||||||
shadowOf(Looper.getMainLooper()).idle()
|
Shadows.shadowOf(Looper.getMainLooper()).idle()
|
||||||
Mockito.mock(LocationPicker::class.java)
|
Mockito.mock(LocationPicker::class.java)
|
||||||
val intent = Mockito.mock(Intent::class.java)
|
val intent = Mockito.mock(Intent::class.java)
|
||||||
val cameraPosition = Mockito.mock(CameraPosition::class.java)
|
val cameraPosition = Mockito.mock(CameraPosition::class.java)
|
||||||
|
|
@ -427,7 +395,7 @@ class UploadMediaDetailFragmentUnitTest {
|
||||||
@Test
|
@Test
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
fun testRememberedZoomLevelOnNull() {
|
fun testRememberedZoomLevelOnNull() {
|
||||||
shadowOf(Looper.getMainLooper()).idle()
|
Shadows.shadowOf(Looper.getMainLooper()).idle()
|
||||||
Whitebox.setInternalState(fragment, "defaultKvStore", defaultKvStore)
|
Whitebox.setInternalState(fragment, "defaultKvStore", defaultKvStore)
|
||||||
`when`(uploadItem.gpsCoords).thenReturn(null)
|
`when`(uploadItem.gpsCoords).thenReturn(null)
|
||||||
`when`(defaultKvStore.getString(LAST_ZOOM)).thenReturn("13.0")
|
`when`(defaultKvStore.getString(LAST_ZOOM)).thenReturn("13.0")
|
||||||
|
|
@ -443,7 +411,7 @@ class UploadMediaDetailFragmentUnitTest {
|
||||||
@Test
|
@Test
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
fun testRememberedZoomLevelOnNotNull() {
|
fun testRememberedZoomLevelOnNotNull() {
|
||||||
shadowOf(Looper.getMainLooper()).idle()
|
Shadows.shadowOf(Looper.getMainLooper()).idle()
|
||||||
`when`(uploadItem.gpsCoords).thenReturn(imageCoordinates)
|
`when`(uploadItem.gpsCoords).thenReturn(imageCoordinates)
|
||||||
`when`(imageCoordinates.decLatitude).thenReturn(8.0)
|
`when`(imageCoordinates.decLatitude).thenReturn(8.0)
|
||||||
`when`(imageCoordinates.decLongitude).thenReturn(-8.0)
|
`when`(imageCoordinates.decLongitude).thenReturn(-8.0)
|
||||||
|
|
@ -456,4 +424,4 @@ class UploadMediaDetailFragmentUnitTest {
|
||||||
val shadowIntent: ShadowIntent = shadowOf(startedIntent)
|
val shadowIntent: ShadowIntent = shadowOf(startedIntent)
|
||||||
Assert.assertEquals(shadowIntent.intentClass, LocationPickerActivity::class.java)
|
Assert.assertEquals(shadowIntent.intentClass, LocationPickerActivity::class.java)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue