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 1ae7150e68..8f1f1e7b00 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 @@ -14,6 +14,7 @@ import android.os.Bundle import android.provider.Settings import android.view.View import android.widget.CheckBox +import androidx.activity.OnBackPressedCallback import androidx.appcompat.app.AlertDialog import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager @@ -122,7 +123,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C /** * Set the value of the showPermissionDialog variable. * - * @param showPermissionsDialog `true` to indicate to show + * @property isShowPermissionsDialog `true` to indicate to show * Permissions Dialog if permissions are missing, `false` otherwise. */ /** @@ -166,6 +167,8 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C private var _binding: ActivityUploadBinding? = null private val binding: ActivityUploadBinding get() = _binding!! + private lateinit var onBackPressedCallback: OnBackPressedCallback + @SuppressLint("CheckResult") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -173,6 +176,23 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C _binding = ActivityUploadBinding.inflate(layoutInflater) setContentView(binding.root) + // Overrides the back button to make sure the user is prepared to lose their progress + onBackPressedCallback = object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + showAlertDialog( + this@UploadActivity, + getString(R.string.back_button_warning), + getString(R.string.back_button_warning_desc), + getString(R.string.back_button_continue), + getString(R.string.back_button_warning), + null + ) { + finish() + } + } + } + onBackPressedDispatcher.addCallback(this, onBackPressedCallback) + /* If Configuration of device is changed then get the new fragments created by the system and populate the fragments ArrayList @@ -187,7 +207,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C } init() - binding.rlContainerTitle.setOnClickListener { v: View? -> onRlContainerTitleClicked() } + binding.rlContainerTitle.setOnClickListener { _: View? -> onRlContainerTitleClicked() } nearbyPopupAnswers = mutableMapOf() //getting the current dpi of the device and if it is less than 320dp i.e. overlapping //threshold, thumbnails automatically minimizes @@ -201,7 +221,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C } locationManager!!.requestLocationUpdatesFromProvider(LocationManager.GPS_PROVIDER) locationManager!!.requestLocationUpdatesFromProvider(LocationManager.NETWORK_PROVIDER) - store = BasicKvStore(this, storeNameForCurrentUploadImagesSize).apply { + store = BasicKvStore(this, STORE_NAME_FOR_CURRENT_UPLOAD_IMAGE_SIZE).apply { clearAll() } checkStoragePermissions() @@ -241,7 +261,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C override fun onPageSelected(position: Int) { currentSelectedPosition = position - if (position >= uploadableFiles!!.size) { + if (position >= uploadableFiles.size) { binding.cvContainerTopCard.visibility = View.GONE } else { thumbnailsAdapter!!.notifyDataSetChanged() @@ -274,7 +294,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .filter { result: Boolean? -> result!! } - .subscribe { result: Boolean? -> + .subscribe { _: Boolean? -> showAlertDialog( this, getString(R.string.block_notification_title), @@ -284,7 +304,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C }) } - fun checkStoragePermissions() { + private fun checkStoragePermissions() { // Check if all required permissions are granted val hasAllPermissions = hasPermission(this, PERMISSIONS_STORAGE) val hasPartialAccess = hasPartialAccess(this) @@ -355,7 +375,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C showLongToast(this, messageResourceId) } - override fun getUploadableFiles(): List? { + override fun getUploadableFiles(): List { return uploadableFiles } @@ -375,8 +395,8 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C binding.tvTopCardTitle.text = resources .getQuantityString( R.plurals.upload_count_title, - uploadableFiles!!.size, - uploadableFiles!!.size + uploadableFiles.size, + uploadableFiles.size ) } @@ -444,15 +464,16 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C receiveInternalSharedItems() } - if (uploadableFiles == null || uploadableFiles!!.isEmpty()) { + if (uploadableFiles.isEmpty()) { handleNullMedia() } else { //Show thumbnails - if (uploadableFiles!!.size > 1) { - if (!defaultKvStore.getBoolean("hasAlreadyLaunchedCategoriesDialog")) { //If there is only file, no need to show the image thumbnails + if (uploadableFiles.size > 1) { + if (!defaultKvStore.getBoolean("hasAlreadyLaunchedCategoriesDialog")) { + // If there is only file, no need to show the image thumbnails showAlertDialogForCategories() } - if (uploadableFiles!!.size > 3 && + if (uploadableFiles.size > 3 && !defaultKvStore.getBoolean("hasAlreadyLaunchedBigMultiupload") ) { showAlertForBattery() @@ -464,8 +485,8 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C binding.tvTopCardTitle.text = resources .getQuantityString( R.plurals.upload_count_title, - uploadableFiles!!.size, - uploadableFiles!!.size + uploadableFiles.size, + uploadableFiles.size ) @@ -474,7 +495,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C } - for (uploadableFile in uploadableFiles!!) { + for (uploadableFile in uploadableFiles) { val uploadMediaDetailFragment = UploadMediaDetailFragment() if (!uploadIsOfAPlace) { @@ -497,8 +518,8 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C object : UploadMediaDetailFragmentCallback { override fun deletePictureAtIndex(index: Int) { store!!.putInt( - keyForCurrentUploadImagesSize, - (store!!.getInt(keyForCurrentUploadImagesSize) - 1) + KEY_FOR_CURRENT_UPLOAD_IMAGE_SIZE, + (store!!.getInt(KEY_FOR_CURRENT_UPLOAD_IMAGE_SIZE) - 1) ) presenter!!.deletePictureAtIndex(index) } @@ -576,11 +597,11 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C fragments!!.add(mediaLicenseFragment!!) } else { for (i in 1 until fragments!!.size) { - fragments!![i]!!.callback = object : UploadBaseFragment.Callback { + fragments!![i].callback = object : UploadBaseFragment.Callback { override fun onNextButtonClicked(index: Int) { if (index < fragments!!.size - 1) { binding.vpUpload.setCurrentItem(index + 1, false) - fragments!![index + 1]!!.onBecameVisible() + fragments!![index + 1].onBecameVisible() (binding.rvThumbnails.layoutManager as LinearLayoutManager) .scrollToPositionWithOffset( if ((index > 0)) index - 1 else 0, @@ -594,7 +615,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C override fun onPreviousButtonClicked(index: Int) { if (index != 0) { binding.vpUpload.setCurrentItem(index - 1, true) - fragments!![index - 1]!!.onBecameVisible() + fragments!![index - 1].onBecameVisible() (binding.rvThumbnails.layoutManager as LinearLayoutManager) .scrollToPositionWithOffset( if ((index > 3)) index - 2 else 0, @@ -632,11 +653,12 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C binding.vpUpload.offscreenPageLimit = fragments!!.size } // Saving size of uploadableFiles - store!!.putInt(keyForCurrentUploadImagesSize, uploadableFiles!!.size) + store!!.putInt(KEY_FOR_CURRENT_UPLOAD_IMAGE_SIZE, uploadableFiles.size) } /** - * Changes current image when one image upload is cancelled, to highlight next image in the top thumbnail. + * Changes current image when one image upload is cancelled, to highlight next image in the top + * thumbnail. * Fixes: [Issue](https://github.com/commons-app/apps-android-commons/issues/5511) * * @param index Index of image to be removed @@ -690,10 +712,10 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C uploadableFiles = mutableListOf().apply { addAll(intent.getParcelableArrayListExtra(EXTRA_FILES) ?: emptyList()) } - isMultipleFilesSelected = uploadableFiles!!.size > 1 - Timber.i("Received multiple upload %s", uploadableFiles!!.size) + isMultipleFilesSelected = uploadableFiles.size > 1 + Timber.i("Received multiple upload %s", uploadableFiles.size) - place = intent.getParcelableExtra(PLACE_OBJECT) + place = intent.getParcelableExtra(PLACE_OBJECT) prevLocation = intent.getParcelableExtra(LOCATION_BEFORE_IMAGE_CAPTURE) isInAppCameraUpload = intent.getBooleanExtra(IN_APP_CAMERA_UPLOAD, false) resetDirectPrefs() @@ -724,7 +746,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C override fun onNextButtonClicked(index: Int) { if (index < fragments!!.size - 1) { binding.vpUpload.setCurrentItem(index + 1, false) - fragments!![index + 1]!!.onBecameVisible() + fragments!![index + 1].onBecameVisible() (binding.rvThumbnails.layoutManager as LinearLayoutManager) .scrollToPositionWithOffset(if ((index > 0)) index - 1 else 0, 0) if (index < fragments!!.size - 4) { @@ -739,10 +761,10 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C override fun onPreviousButtonClicked(index: Int) { if (index != 0) { binding.vpUpload.setCurrentItem(index - 1, true) - fragments!![index - 1]!!.onBecameVisible() + fragments!![index - 1].onBecameVisible() (binding.rvThumbnails.layoutManager as LinearLayoutManager) .scrollToPositionWithOffset(if ((index > 3)) index - 2 else 0, 0) - if ((index != 1) && ((index - 1) < uploadableFiles!!.size)) { + if ((index != 1) && ((index - 1) < uploadableFiles.size)) { // Shows the top card if it was hidden because of the last image being deleted and // now the user has hit previous button to go back to the media details showHideTopCard(true) @@ -750,7 +772,10 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C } } - override fun onThumbnailDeleted(position: Int) = presenter!!.deletePictureAtIndex(position) + override fun onThumbnailDeleted(position: Int) { + presenter!!.deletePictureAtIndex(position) + thumbnailsAdapter?.notifyItemRemoved(position) + } /** * The adapter used to show image upload intermediate fragments @@ -777,11 +802,11 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C } - fun onRlContainerTitleClicked() { + private fun onRlContainerTitleClicked() { binding.rvThumbnails.visibility = if (isTitleExpanded) View.GONE else View.VISIBLE isTitleExpanded = !isTitleExpanded - binding.ibToggleTopCard.rotation = binding.ibToggleTopCard.rotation + 180 + binding.ibToggleTopCard.rotation += 180 } override fun onDestroy() { @@ -798,20 +823,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C if (uploadCategoriesFragment != null) { uploadCategoriesFragment!!.callback = null } - } - - /** - * Overrides the back button to make sure the user is prepared to lose their progress - */ - override fun onBackPressed() { - showAlertDialog( - this, - getString(R.string.back_button_warning), - getString(R.string.back_button_warning_desc), - getString(R.string.back_button_continue), - getString(R.string.back_button_warning), - null - ) { finish() } + onBackPressedCallback.remove() } /** @@ -831,7 +843,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C .setView(view) .setTitle(getString(R.string.multiple_files_depiction_header)) .setMessage(getString(R.string.multiple_files_depiction)) - .setPositiveButton("OK") { dialog: DialogInterface?, which: Int -> + .setPositiveButton("OK") { _: DialogInterface?, _: Int -> if (checkBox.isChecked) { // Save the user's choice to not show the dialog again defaultKvStore.putBoolean("hasAlreadyLaunchedCategoriesDialog", true) @@ -865,14 +877,14 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C getString(R.string.cancel), { /* Since opening the right settings page might be device dependent, using - https://github.com/WaseemSabir/BatteryPermissionHelper - directly appeared like a promising idea. - However, this simply closed the popup and did not make - the settings page appear on a Pixel as well as a Xiaomi device. - Used the standard intent instead of using this library as - it shows a list of all the apps on the device and allows users to - turn battery optimisation off. - */ + https://github.com/WaseemSabir/BatteryPermissionHelper + directly appeared like a promising idea. + However, this simply closed the popup and did not make + the settings page appear on a Pixel as well as a Xiaomi device. + Used the standard intent instead of using this library as + it shows a list of all the apps on the device and allows users to + turn battery optimisation off. + */ val batteryOptimisationSettingsIntent = Intent( Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS ) @@ -910,7 +922,8 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C Also, location information is discarded if the difference between current location and location recorded just before capturing the image is greater than 100 meters */ - if (isLocationTagUnchecked || locationDifference > 100 || !defaultKvStore.getBoolean("inAppCameraLocationPref") + if (isLocationTagUnchecked || locationDifference > 100 + || !defaultKvStore.getBoolean("inAppCameraLocationPref") || !isInAppCameraUpload ) { currLocation = null @@ -931,8 +944,8 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C @JvmField var nearbyPopupAnswers: MutableMap? = null - const val keyForCurrentUploadImagesSize: String = "CurrentUploadImagesSize" - const val storeNameForCurrentUploadImagesSize: String = "CurrentUploadImageQualities" + const val KEY_FOR_CURRENT_UPLOAD_IMAGE_SIZE: String = "CurrentUploadImagesSize" + const val STORE_NAME_FOR_CURRENT_UPLOAD_IMAGE_SIZE: String = "CurrentUploadImageQualities" /** * Sets the flag indicating whether the upload is of a specific place. 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 92a46b92a8..0762622f4c 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 @@ -528,7 +528,7 @@ class UploadMediaDetailFragment : UploadBaseFragment(), UploadMediaDetailsContra basicKvStore!!.putBoolean(keyForShowingAlertDialog, false) if (isInternetConnectionEstablished(requireActivity())) { val sizeOfUploads = basicKvStore!!.getInt( - UploadActivity.keyForCurrentUploadImagesSize + UploadActivity.KEY_FOR_CURRENT_UPLOAD_IMAGE_SIZE ) for (i in indexOfFragment until sizeOfUploads) { presenter.getImageQuality( 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 55cead3708..eccafdcbd4 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 @@ -310,7 +310,7 @@ class UploadMediaPresenter @Inject constructor( private fun storeImageQuality( imageResult: Int, uploadItemIndex: Int, activity: Activity, uploadItem: UploadItem ) { - val store = BasicKvStore(activity, UploadActivity.storeNameForCurrentUploadImagesSize) + val store = BasicKvStore(activity, UploadActivity.STORE_NAME_FOR_CURRENT_UPLOAD_IMAGE_SIZE) val value = store.getString(UPLOAD_QUALITIES_KEY, null) try { val jsonObject = value.asJsonObject().apply { @@ -339,7 +339,7 @@ class UploadMediaPresenter @Inject constructor( */ override fun checkImageQuality(uploadItem: UploadItem, index: Int) { if ((uploadItem.imageQuality != IMAGE_OK) && (uploadItem.imageQuality != IMAGE_KEEP)) { - val value = basicKvStoreFactory(UploadActivity.storeNameForCurrentUploadImagesSize) + val value = basicKvStoreFactory(UploadActivity.STORE_NAME_FOR_CURRENT_UPLOAD_IMAGE_SIZE) .getString(UPLOAD_QUALITIES_KEY, null) try { val imageQuality = value.asJsonObject()["UploadItem$index"] as Int @@ -363,7 +363,7 @@ class UploadMediaPresenter @Inject constructor( * @param index Index of the UploadItem which was deleted */ override fun updateImageQualitiesJSON(size: Int, index: Int) { - val value = basicKvStoreFactory(UploadActivity.storeNameForCurrentUploadImagesSize) + val value = basicKvStoreFactory(UploadActivity.STORE_NAME_FOR_CURRENT_UPLOAD_IMAGE_SIZE) .getString(UPLOAD_QUALITIES_KEY, null) try { val jsonObject = value.asJsonObject().apply { @@ -372,7 +372,7 @@ class UploadMediaPresenter @Inject constructor( } remove("UploadItem" + (size - 1)) } - basicKvStoreFactory(UploadActivity.storeNameForCurrentUploadImagesSize) + basicKvStoreFactory(UploadActivity.STORE_NAME_FOR_CURRENT_UPLOAD_IMAGE_SIZE) .putString(UPLOAD_QUALITIES_KEY, jsonObject.toString()) } catch (e: Exception) { Timber.e(e) diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadActivityUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadActivityUnitTests.kt index 1173d09b09..97fe688625 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadActivityUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadActivityUnitTests.kt @@ -262,15 +262,4 @@ class UploadActivityUnitTests { method.isAccessible = true method.invoke(activity) } - - @Test - @Throws(Exception::class) - fun testOnBackPressed() { - val method: Method = - UploadActivity::class.java.getDeclaredMethod( - "onBackPressed", - ) - method.isAccessible = true - method.invoke(activity) - } }