-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Fix: Prevent crash when pausing/resuming uploads after screen rotation #6532
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 9 commits
8352bc5
38cd528
33d3e44
8f66028
e079de0
e5a4fd3
7522803
81b59a0
7aa1c0f
b79f540
3dbb031
4f7033b
2e13a02
7b18d39
6b3be73
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,7 +28,7 @@ class PendingUploadsFragment : | |
| CommonsDaggerSupportFragment(), | ||
| PendingUploadsContract.View, | ||
| PendingUploadsAdapter.Callback { | ||
| @Inject | ||
| @Inject | ||
| lateinit var pendingUploadsPresenter: PendingUploadsPresenter | ||
|
|
||
| private lateinit var binding: FragmentPendingUploadsBinding | ||
|
|
@@ -60,10 +60,6 @@ class PendingUploadsFragment : | |
| return binding.root | ||
| } | ||
|
|
||
| fun initAdapter() { | ||
| adapter = PendingUploadsAdapter(this) | ||
| } | ||
|
|
||
| override fun onViewCreated( | ||
| view: View, | ||
| savedInstanceState: Bundle?, | ||
|
|
@@ -72,6 +68,10 @@ class PendingUploadsFragment : | |
| initRecyclerView() | ||
| } | ||
|
|
||
| fun initAdapter() { | ||
| adapter = PendingUploadsAdapter(this) | ||
| } | ||
|
|
||
| /** | ||
| * Initializes the recycler view. | ||
| */ | ||
|
|
@@ -81,38 +81,38 @@ class PendingUploadsFragment : | |
| pendingUploadsPresenter.setup() | ||
| pendingUploadsPresenter.totalContributionList | ||
| .observe(viewLifecycleOwner) { list: PagedList<Contribution> -> | ||
| contributionsSize = list.size | ||
| contributionsList = mutableListOf() | ||
| var pausedOrQueuedUploads = 0 | ||
| list.forEach { | ||
| if (it != null) { | ||
| if (it.state == STATE_PAUSED || | ||
| it.state == STATE_QUEUED || | ||
| it.state == STATE_IN_PROGRESS | ||
| ) { | ||
| contributionsList.add(it) | ||
| } | ||
| if (it.state == STATE_PAUSED || it.state == STATE_QUEUED) { | ||
| pausedOrQueuedUploads++ | ||
| contributionsSize = list.size | ||
| contributionsList = mutableListOf() | ||
| var pausedOrQueuedUploads = 0 | ||
| list.forEach { | ||
| if (it != null) { | ||
| if (it.state == STATE_PAUSED || | ||
| it.state == STATE_QUEUED || | ||
| it.state == STATE_IN_PROGRESS | ||
| ) { | ||
| contributionsList.add(it) | ||
| } | ||
| if (it.state == STATE_PAUSED || it.state == 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 uploads left") | ||
| if ((pausedOrQueuedUploads == contributionsSize) || CommonsApplication.isPaused) { | ||
| uploadProgressActivity.setPausedIcon(true) | ||
| if (contributionsSize == 0) { | ||
| binding.nopendingTextView.visibility = View.VISIBLE | ||
| binding.pendingUplaodsLl.visibility = View.GONE | ||
| uploadProgressActivity.hidePendingIcons() | ||
| } else { | ||
| uploadProgressActivity.setPausedIcon(false) | ||
| binding.nopendingTextView.visibility = View.GONE | ||
| binding.pendingUplaodsLl.visibility = View.VISIBLE | ||
| adapter.submitList(list) | ||
| binding.progressTextView.setText("$contributionsSize uploads left") | ||
| if ((pausedOrQueuedUploads == contributionsSize) || CommonsApplication.isPaused) { | ||
| uploadProgressActivity.setPausedIcon(true) | ||
| } else { | ||
| uploadProgressActivity.setPausedIcon(false) | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -140,19 +140,26 @@ class PendingUploadsFragment : | |
| /** | ||
| * Restarts all the paused uploads. | ||
| */ | ||
| fun restartUploads() = pendingUploadsPresenter.restartUploads( | ||
| contributionsList, 0, requireContext().applicationContext | ||
| ) | ||
| fun restartUploads() { | ||
| pendingUploadsPresenter.restartUploads( | ||
| contributionsList, 0, requireContext().applicationContext | ||
| ) | ||
| } | ||
|
|
||
| /** | ||
| * Pauses all the ongoing uploads. | ||
| */ | ||
| fun pauseUploads() = pendingUploadsPresenter.pauseUploads() | ||
| fun pauseUploads() { | ||
| pendingUploadsPresenter.pauseUploads() | ||
| } | ||
|
|
||
| /** | ||
| * Cancels all the uploads after getting a confirmation from the user using Dialog. | ||
| */ | ||
| fun deleteUploads() { | ||
| // the check below is no longer needed but kept for original logic | ||
|
||
| if (!::pendingUploadsPresenter.isInitialized) return | ||
|
|
||
| val activity = requireActivity() | ||
| val locale = Locale.getDefault() | ||
| showAlertDialog( | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,8 +21,7 @@ import javax.inject.Inject | |
| */ | ||
| class UploadProgressActivity : BaseActivity() { | ||
| private lateinit var binding: ActivityUploadProgressBinding | ||
| private var pendingUploadsFragment: PendingUploadsFragment? = null | ||
| private var failedUploadsFragment: FailedUploadsFragment? = null | ||
| // fields for the fragments are removed as retrieval is done via FragmentManager | ||
| var viewPagerAdapter: ViewPagerAdapter? = null | ||
| var menu: Menu? = null | ||
|
|
||
|
|
@@ -68,18 +67,36 @@ class UploadProgressActivity : BaseActivity() { | |
| setTabs() | ||
| } | ||
|
|
||
| // FIX: the helelper to retrieve the current, non-stale PendingUploadsFragment instance. | ||
| private fun getPendingUploadsFragment(): PendingUploadsFragment? { | ||
| return supportFragmentManager.findFragmentByTag( | ||
| "android:switcher:${R.id.upload_progress_view_pager}:${0}", | ||
|
||
| ) as? PendingUploadsFragment | ||
| } | ||
|
|
||
|
|
||
| // FIX: helper to retrieve the current, non-stale FailedUploadsFragment instance. | ||
| private fun getFailedUploadsFragment(): FailedUploadsFragment? { | ||
| return supportFragmentManager.findFragmentByTag( | ||
| "android:switcher:${R.id.upload_progress_view_pager}:${1}", | ||
| ) as? FailedUploadsFragment | ||
| } | ||
|
|
||
| /** | ||
| * Initializes and sets up the tabs data by creating instances of `PendingUploadsFragment` | ||
| * and `FailedUploadsFragment`, adds them to the `fragmentList`, and assigns corresponding | ||
| * titles from resources to the `titleList`. | ||
| */ | ||
| fun setTabs() { | ||
| pendingUploadsFragment = PendingUploadsFragment() | ||
| failedUploadsFragment = FailedUploadsFragment() | ||
| val pendingUploadsFragment: Fragment | ||
| val failedUploadsFragment: Fragment | ||
| // using the FragmentManager lookup to get the correct, current instances after the rotation | ||
| pendingUploadsFragment = getPendingUploadsFragment() ?: PendingUploadsFragment() | ||
| failedUploadsFragment = getFailedUploadsFragment() ?: FailedUploadsFragment() | ||
|
|
||
| viewPagerAdapter!!.setTabs( | ||
| R.string.pending to pendingUploadsFragment!!, | ||
| R.string.failed to failedUploadsFragment!! | ||
| R.string.pending to pendingUploadsFragment, | ||
| R.string.failed to failedUploadsFragment | ||
| ) | ||
| viewPagerAdapter!!.notifyDataSetChanged() | ||
| } | ||
|
|
@@ -119,7 +136,8 @@ class UploadProgressActivity : BaseActivity() { | |
| getString(R.string.pause), | ||
| ).setIcon(R.drawable.pause_icon) | ||
| .setOnMenuItemClickListener { | ||
| pendingUploadsFragment!!.pauseUploads() | ||
| // FIX: retrive and use the current fragment instance with safe call to avoid the NPE if retrieval fails | ||
| getPendingUploadsFragment()?.pauseUploads() | ||
| setPausedIcon(true) | ||
| true | ||
| }.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM) | ||
|
|
@@ -133,7 +151,8 @@ class UploadProgressActivity : BaseActivity() { | |
| getString(R.string.cancel), | ||
| ).setIcon(R.drawable.ic_cancel_upload) | ||
| .setOnMenuItemClickListener { | ||
| pendingUploadsFragment!!.deleteUploads() | ||
| // FIX: retrive and use the current fragment instance | ||
| getPendingUploadsFragment()?.deleteUploads() | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will we evaluate this for every operation? Could you please elaborate if there are any use cases apart from configuration changes that need this? I thought adding it to |
||
| true | ||
| }.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM) | ||
| } | ||
|
|
@@ -147,7 +166,8 @@ class UploadProgressActivity : BaseActivity() { | |
| getString(R.string.resume), | ||
| ).setIcon(R.drawable.play_icon) | ||
| .setOnMenuItemClickListener { | ||
| pendingUploadsFragment!!.restartUploads() | ||
| // FIX: retrive and use the current fragment instance | ||
| getPendingUploadsFragment()?.restartUploads() | ||
| setPausedIcon(false) | ||
| true | ||
| }.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM) | ||
|
|
@@ -161,7 +181,7 @@ class UploadProgressActivity : BaseActivity() { | |
| .add(Menu.NONE, R.id.retry_icon, Menu.NONE, getString(R.string.retry)) | ||
| .setIcon(R.drawable.ic_refresh_24dp) | ||
| .setOnMenuItemClickListener { | ||
| failedUploadsFragment!!.restartUploads() | ||
| getFailedUploadsFragment()?.restartUploads() | ||
| true | ||
| }.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM) | ||
| } | ||
|
|
@@ -174,7 +194,7 @@ class UploadProgressActivity : BaseActivity() { | |
| getString(R.string.cancel), | ||
| ).setIcon(R.drawable.ic_cancel_upload) | ||
| .setOnMenuItemClickListener { | ||
| failedUploadsFragment!!.deleteUploads() | ||
| getFailedUploadsFragment()?.deleteUploads() | ||
| true | ||
| }.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM) | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My understanding was that we don't need any changes in this entire file, is there anything that I'm missing out?😕