diff --git a/app/src/main/java/fr/free/nrw/commons/upload/PendingUploadsFragment.kt b/app/src/main/java/fr/free/nrw/commons/upload/PendingUploadsFragment.kt index 4d79bc88e8..1237db4615 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/PendingUploadsFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/PendingUploadsFragment.kt @@ -1,194 +1,203 @@ -package fr.free.nrw.commons.upload - -import android.content.Context -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.paging.PagedList -import androidx.recyclerview.widget.LinearLayoutManager -import fr.free.nrw.commons.CommonsApplication -import fr.free.nrw.commons.R -import fr.free.nrw.commons.contributions.Contribution -import fr.free.nrw.commons.databinding.FragmentPendingUploadsBinding -import fr.free.nrw.commons.di.CommonsDaggerSupportFragment -import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog -import fr.free.nrw.commons.utils.ViewUtil -import java.util.Locale -import javax.inject.Inject - -/** - * Fragment for showing pending uploads in Upload Progress Activity. This fragment provides - * functionality for the user to pause uploads. - */ -class PendingUploadsFragment : - CommonsDaggerSupportFragment(), - PendingUploadsContract.View, - PendingUploadsAdapter.Callback { - @Inject - lateinit var pendingUploadsPresenter: PendingUploadsPresenter - - private lateinit var binding: FragmentPendingUploadsBinding - - private lateinit var uploadProgressActivity: UploadProgressActivity - - private lateinit var adapter: PendingUploadsAdapter - - private var contributionsSize = 0 - var contributionsList = ArrayList() - - override fun onAttach(context: Context) { - super.onAttach(context) - if (context is UploadProgressActivity) { - uploadProgressActivity = context - } - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle?, - ): View? { - super.onCreate(savedInstanceState) - binding = FragmentPendingUploadsBinding.inflate(inflater, container, false) - pendingUploadsPresenter.onAttachView(this) - initAdapter() - return binding.root - } - - fun initAdapter() { - adapter = PendingUploadsAdapter(this) - } - - override fun onViewCreated( - view: View, - savedInstanceState: Bundle?, - ) { - super.onViewCreated(view, savedInstanceState) - initRecyclerView() - } - - /** - * Initializes the recycler view. - */ - fun initRecyclerView() { - binding.pendingUploadsRecyclerView.setLayoutManager(LinearLayoutManager(this.context)) - binding.pendingUploadsRecyclerView.adapter = adapter - pendingUploadsPresenter!!.setup() - pendingUploadsPresenter!!.totalContributionList.observe( - viewLifecycleOwner, - ) { list: PagedList -> - contributionsSize = list.size - contributionsList = ArrayList() - var pausedOrQueuedUploads = 0 - list.forEach { - if (it != null) { - if (it.state == Contribution.STATE_PAUSED || - it.state == Contribution.STATE_QUEUED || - it.state == Contribution.STATE_IN_PROGRESS - ) { - contributionsList.add(it) - } - if (it.state == Contribution.STATE_PAUSED || - it.state == Contribution.STATE_QUEUED - ) { - pausedOrQueuedUploads++ - } - } - } - if (contributionsSize == 0) { - binding.nopendingTextView.visibility = View.VISIBLE - binding.pendingUplaodsLl.visibility = View.GONE - uploadProgressActivity.hidePendingIcons() - } else { - binding.nopendingTextView.visibility = View.GONE - binding.pendingUplaodsLl.visibility = View.VISIBLE - adapter.submitList(list) - binding.progressTextView.setText(contributionsSize.toString() + " uploads left") - if ((pausedOrQueuedUploads == contributionsSize) || CommonsApplication.isPaused) { - uploadProgressActivity.setPausedIcon(true) - } else { - uploadProgressActivity.setPausedIcon(false) - } - } - } - } - - /** - * Cancels a specific upload after getting a confirmation from the user using Dialog. - */ - override fun deleteUpload(contribution: Contribution?) { - showAlertDialog( - requireActivity(), - String.format( - Locale.getDefault(), - requireActivity().getString(R.string.cancelling_upload), - ), - String.format( - Locale.getDefault(), - requireActivity().getString(R.string.cancel_upload_dialog), - ), - String.format(Locale.getDefault(), requireActivity().getString(R.string.yes)), - String.format(Locale.getDefault(), requireActivity().getString(R.string.no)), - { - ViewUtil.showShortToast(context, R.string.cancelling_upload) - pendingUploadsPresenter.deleteUpload( - contribution, - this.requireContext().applicationContext, - ) - }, - {}, - ) - } - - /** - * Restarts all the paused uploads. - */ - fun restartUploads() { - if (contributionsList != null) { - pendingUploadsPresenter.restartUploads( - contributionsList, - 0, - this.requireContext().applicationContext, - ) - } - } - - /** - * Pauses all the ongoing uploads. - */ - fun pauseUploads() { - pendingUploadsPresenter.pauseUploads() - } - - /** - * Cancels all the uploads after getting a confirmation from the user using Dialog. - */ - fun deleteUploads() { - showAlertDialog( - requireActivity(), - String.format( - Locale.getDefault(), - requireActivity().getString(R.string.cancelling_all_the_uploads), - ), - String.format( - Locale.getDefault(), - requireActivity().getString(R.string.are_you_sure_that_you_want_cancel_all_the_uploads), - ), - String.format(Locale.getDefault(), requireActivity().getString(R.string.yes)), - String.format(Locale.getDefault(), requireActivity().getString(R.string.no)), - { - ViewUtil.showShortToast(context, R.string.cancelling_upload) - uploadProgressActivity.hidePendingIcons() - pendingUploadsPresenter.deleteUploads( - listOf( - Contribution.STATE_QUEUED, - Contribution.STATE_IN_PROGRESS, - Contribution.STATE_PAUSED, - ), - ) - }, - {}, - ) - } -} +package fr.free.nrw.commons.upload + +import android.content.Context +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.paging.PagedList +import androidx.recyclerview.widget.LinearLayoutManager +import fr.free.nrw.commons.CommonsApplication +import fr.free.nrw.commons.R +import fr.free.nrw.commons.contributions.Contribution +import fr.free.nrw.commons.databinding.FragmentPendingUploadsBinding +import fr.free.nrw.commons.di.CommonsDaggerSupportFragment +import fr.free.nrw.commons.upload.worker.WorkRequestHelper +import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog +import fr.free.nrw.commons.utils.ViewUtil +import java.util.Locale +import javax.inject.Inject + +/** + * Fragment for showing pending uploads in Upload Progress Activity. This fragment provides + * functionality for the user to pause uploads. + */ +class PendingUploadsFragment : + CommonsDaggerSupportFragment(), + PendingUploadsContract.View, + PendingUploadsAdapter.Callback { + @Inject + lateinit var pendingUploadsPresenter: PendingUploadsPresenter + + private lateinit var binding: FragmentPendingUploadsBinding + + private lateinit var uploadProgressActivity: UploadProgressActivity + + private lateinit var adapter: PendingUploadsAdapter + + private var contributionsSize = 0 + var contributionsList = ArrayList() + + override fun onAttach(context: Context) { + super.onAttach(context) + if (context is UploadProgressActivity) { + uploadProgressActivity = context + } + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, + ): View? { + super.onCreate(savedInstanceState) + binding = FragmentPendingUploadsBinding.inflate(inflater, container, false) + pendingUploadsPresenter.onAttachView(this) + initAdapter() + return binding.root + } + + fun initAdapter() { + adapter = PendingUploadsAdapter(this) + } + + override fun onViewCreated( + view: View, + savedInstanceState: Bundle?, + ) { + super.onViewCreated(view, savedInstanceState) + initRecyclerView() + } + + /** + * Initializes the recycler view. + */ + fun initRecyclerView() { + binding.pendingUploadsRecyclerView.setLayoutManager(LinearLayoutManager(this.context)) + binding.pendingUploadsRecyclerView.adapter = adapter + pendingUploadsPresenter!!.setup() + pendingUploadsPresenter!!.totalContributionList.observe( + viewLifecycleOwner, + ) { list: PagedList -> + contributionsSize = list.size + contributionsList = ArrayList() + var pausedOrQueuedUploads = 0 + list.forEach { + if (it != null) { + if (it.state == Contribution.STATE_PAUSED || + it.state == Contribution.STATE_QUEUED || + it.state == Contribution.STATE_IN_PROGRESS + ) { + contributionsList.add(it) + } + if (it.state == Contribution.STATE_PAUSED || + it.state == Contribution.STATE_QUEUED + ) { + pausedOrQueuedUploads++ + } + } + } + if (contributionsSize == 0) { + binding.nopendingTextView.visibility = View.VISIBLE + binding.pendingUplaodsLl.visibility = View.GONE + uploadProgressActivity.hidePendingIcons() + } else { + binding.nopendingTextView.visibility = View.GONE + binding.pendingUplaodsLl.visibility = View.VISIBLE + adapter.submitList(list) + binding.progressTextView.setText(contributionsSize.toString() + " uploads left") + + if ((pausedOrQueuedUploads == contributionsSize) || CommonsApplication.isPaused) { + //As this function would be triggered multiple times by one tap + //Add new checking to enable/ disable the pause button for better UI + if (!WorkRequestHelper.Companion.getisUploadWorkerRunning()) { + uploadProgressActivity.setPausedIcon(true) + } + + } else { + if (WorkRequestHelper.Companion.getisUploadWorkerRunning()) { + uploadProgressActivity.setPausedIcon(false) + } + } + } + } + } + + /** + * Cancels a specific upload after getting a confirmation from the user using Dialog. + */ + override fun deleteUpload(contribution: Contribution?) { + showAlertDialog( + requireActivity(), + String.format( + Locale.getDefault(), + requireActivity().getString(R.string.cancelling_upload), + ), + String.format( + Locale.getDefault(), + requireActivity().getString(R.string.cancel_upload_dialog), + ), + String.format(Locale.getDefault(), requireActivity().getString(R.string.yes)), + String.format(Locale.getDefault(), requireActivity().getString(R.string.no)), + { + ViewUtil.showShortToast(context, R.string.cancelling_upload) + pendingUploadsPresenter.deleteUpload( + contribution, + this.requireContext().applicationContext, + ) + }, + {}, + ) + } + + /** + * Restarts all the paused uploads. + */ + fun restartUploads() { + if (contributionsList != null) { + pendingUploadsPresenter.restartUploads( + contributionsList, + 0, + this.requireContext().applicationContext, + ) + } + } + + /** + * Pauses all the ongoing uploads. + */ + fun pauseUploads() { + pendingUploadsPresenter.pauseUploads() + } + + /** + * Cancels all the uploads after getting a confirmation from the user using Dialog. + */ + fun deleteUploads() { + showAlertDialog( + requireActivity(), + String.format( + Locale.getDefault(), + requireActivity().getString(R.string.cancelling_all_the_uploads), + ), + String.format( + Locale.getDefault(), + requireActivity().getString(R.string.are_you_sure_that_you_want_cancel_all_the_uploads), + ), + String.format(Locale.getDefault(), requireActivity().getString(R.string.yes)), + String.format(Locale.getDefault(), requireActivity().getString(R.string.no)), + { + ViewUtil.showShortToast(context, R.string.cancelling_upload) + uploadProgressActivity.hidePendingIcons() + pendingUploadsPresenter.deleteUploads( + listOf( + Contribution.STATE_QUEUED, + Contribution.STATE_IN_PROGRESS, + Contribution.STATE_PAUSED, + ), + ) + }, + {}, + ) + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/upload/worker/WorkRequestHelper.kt b/app/src/main/java/fr/free/nrw/commons/upload/worker/WorkRequestHelper.kt index 39d59a8d54..ce21fac324 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/worker/WorkRequestHelper.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/worker/WorkRequestHelper.kt @@ -1,74 +1,81 @@ -package fr.free.nrw.commons.upload.worker - -import android.content.Context -import androidx.work.BackoffPolicy -import androidx.work.Constraints -import androidx.work.ExistingWorkPolicy -import androidx.work.NetworkType -import androidx.work.OneTimeWorkRequest -import androidx.work.WorkManager -import androidx.work.WorkRequest.Companion.MIN_BACKOFF_MILLIS -import timber.log.Timber -import java.util.concurrent.TimeUnit - -/** - * Helper class for all the one time work requests - */ -class WorkRequestHelper { - companion object { - private var isUploadWorkerRunning = false - private val lock = Object() - - fun makeOneTimeWorkRequest( - context: Context, - existingWorkPolicy: ExistingWorkPolicy, - ) { - synchronized(lock) { - if (isUploadWorkerRunning) { - Timber.e("UploadWorker is already running. Cannot start another instance.") - return - } else { - Timber.e("Setting isUploadWorkerRunning to true") - isUploadWorkerRunning = true - } - } - - /* Set backoff criteria for the work request - The default backoff policy is EXPONENTIAL, but while testing we found that it - too long for the uploads to finish. So, set the backoff policy as LINEAR with the - minimum backoff delay value of 10 seconds. - - More details on when exactly it is retried: - https://developer.android.com/guide/background/persistent/getting-started/define-work#retries_backoff - */ - val constraints: Constraints = - Constraints - .Builder() - .setRequiredNetworkType(NetworkType.CONNECTED) - .build() - val uploadRequest: OneTimeWorkRequest = - OneTimeWorkRequest - .Builder(UploadWorker::class.java) - .setBackoffCriteria( - BackoffPolicy.LINEAR, - MIN_BACKOFF_MILLIS, - TimeUnit.MILLISECONDS, - ).setConstraints(constraints) - .build() - WorkManager.getInstance(context).enqueueUniqueWork( - UploadWorker::class.java.simpleName, - existingWorkPolicy, - uploadRequest, - ) - } - - /** - * Sets the flag isUploadWorkerRunning to`false` allowing new worker to be started. - */ - fun markUploadWorkerAsStopped() { - synchronized(lock) { - isUploadWorkerRunning = false - } - } - } -} +package fr.free.nrw.commons.upload.worker + +import android.content.Context +import androidx.work.BackoffPolicy +import androidx.work.Constraints +import androidx.work.ExistingWorkPolicy +import androidx.work.NetworkType +import androidx.work.OneTimeWorkRequest +import androidx.work.WorkManager +import androidx.work.WorkRequest.Companion.MIN_BACKOFF_MILLIS +import timber.log.Timber +import java.util.concurrent.TimeUnit + +/** + * Helper class for all the one time work requests + */ +class WorkRequestHelper { + companion object { + private var isUploadWorkerRunning = false + private val lock = Object() + + fun makeOneTimeWorkRequest( + context: Context, + existingWorkPolicy: ExistingWorkPolicy, + ) { + synchronized(lock) { + if (isUploadWorkerRunning) { + Timber.e("UploadWorker is already running. Cannot start another instance.") + return + } else { + Timber.e("Setting isUploadWorkerRunning to true") + isUploadWorkerRunning = true + } + } + + /* Set backoff criteria for the work request + The default backoff policy is EXPONENTIAL, but while testing we found that it + too long for the uploads to finish. So, set the backoff policy as LINEAR with the + minimum backoff delay value of 10 seconds. + + More details on when exactly it is retried: + https://developer.android.com/guide/background/persistent/getting-started/define-work#retries_backoff + */ + val constraints: Constraints = + Constraints + .Builder() + .setRequiredNetworkType(NetworkType.CONNECTED) + .build() + val uploadRequest: OneTimeWorkRequest = + OneTimeWorkRequest + .Builder(UploadWorker::class.java) + .setBackoffCriteria( + BackoffPolicy.LINEAR, + MIN_BACKOFF_MILLIS, + TimeUnit.MILLISECONDS, + ).setConstraints(constraints) + .build() + WorkManager.getInstance(context).enqueueUniqueWork( + UploadWorker::class.java.simpleName, + existingWorkPolicy, + uploadRequest, + ) + } + + /** + * Sets the flag isUploadWorkerRunning to`false` allowing new worker to be started. + */ + fun markUploadWorkerAsStopped() { + synchronized(lock) { + isUploadWorkerRunning = false + } + } + + /** + * Provide a function for other class to get the flag isUploadWorkerRunning + */ + fun getisUploadWorkerRunning(): Boolean { + return isUploadWorkerRunning; + } + } +}