Skip to content

Commit

Permalink
Merge pull request #3339 from streetcomplete/heed_view_lifecycle
Browse files Browse the repository at this point in the history
let the async code in fragments heed the fragment's view lifecycle
  • Loading branch information
westnordost authored Oct 4, 2021
2 parents c821188 + e24b021 commit d677942
Show file tree
Hide file tree
Showing 38 changed files with 181 additions and 212 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.DividerItemDecoration

import de.westnordost.streetcomplete.R
import de.westnordost.streetcomplete.databinding.FragmentChangelogBinding
import de.westnordost.streetcomplete.databinding.RowChangelogBinding
import de.westnordost.streetcomplete.ktx.getYamlObject
import de.westnordost.streetcomplete.ktx.viewBinding
import de.westnordost.streetcomplete.ktx.viewLifecycleScope
import de.westnordost.streetcomplete.view.ListAdapter
import kotlinx.coroutines.*

Expand All @@ -28,7 +28,7 @@ class ChangelogFragment : Fragment(R.layout.fragment_changelog) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
binding.changelogList.addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL))

lifecycleScope.launch {
viewLifecycleScope.launch {
val changelog = readChangelog(resources)
binding.changelogList.adapter = ChangelogAdapter(changelog)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.widget.LinearLayout
import androidx.core.widget.TextViewCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import de.westnordost.streetcomplete.R
import de.westnordost.streetcomplete.databinding.FragmentCreditsBinding
import de.westnordost.streetcomplete.databinding.RowCreditsTranslatorsBinding
import de.westnordost.streetcomplete.ktx.getYamlObject
import de.westnordost.streetcomplete.ktx.viewBinding
import de.westnordost.streetcomplete.ktx.viewLifecycleScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
Expand All @@ -27,7 +27,7 @@ class CreditsFragment : Fragment(R.layout.fragment_credits) {
private val binding by viewBinding(FragmentCreditsBinding::bind)

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
lifecycleScope.launch {
viewLifecycleScope.launch {
addContributorsTo(readMainContributors(), binding.mainCredits)
addContributorsTo(readProjectsContributors(), binding.projectsCredits)
addContributorsTo(readArtContributors(), binding.artCredits)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ package de.westnordost.streetcomplete.controls

import android.content.SharedPreferences
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import de.westnordost.streetcomplete.Injector
import de.westnordost.streetcomplete.Prefs
import de.westnordost.streetcomplete.R
import de.westnordost.streetcomplete.data.UnsyncedChangesCountSource
import de.westnordost.streetcomplete.data.upload.UploadProgressListener
import de.westnordost.streetcomplete.data.upload.UploadProgressSource
import de.westnordost.streetcomplete.data.user.QuestStatisticsDao
import de.westnordost.streetcomplete.ktx.viewLifecycleScope
import kotlinx.coroutines.launch
import javax.inject.Inject

Expand All @@ -24,19 +24,19 @@ class AnswersCounterFragment : Fragment(R.layout.fragment_answers_counter) {
private val answersCounterView get() = view as AnswersCounterView

private val uploadProgressListener = object : UploadProgressListener {
override fun onStarted() { lifecycleScope.launch { updateProgress(true) } }
override fun onFinished() { lifecycleScope.launch { updateProgress(false) } }
override fun onStarted() { viewLifecycleScope.launch { updateProgress(true) } }
override fun onFinished() { viewLifecycleScope.launch { updateProgress(false) } }
}

private val unsyncedChangesCountListener = object : UnsyncedChangesCountSource.Listener {
override fun onIncreased() { lifecycleScope.launch { updateCount(true) }}
override fun onDecreased() { lifecycleScope.launch { updateCount(true) }}
override fun onIncreased() { viewLifecycleScope.launch { updateCount(true) }}
override fun onDecreased() { viewLifecycleScope.launch { updateCount(true) }}
}

private val questStatisticsListener = object : QuestStatisticsDao.Listener {
override fun onAddedOne(questType: String) { lifecycleScope.launch { addCount(+1,true) }}
override fun onSubtractedOne(questType: String) { lifecycleScope.launch { addCount(-1,true) }}
override fun onReplacedAll() { lifecycleScope.launch { updateCount(false) }}
override fun onAddedOne(questType: String) { viewLifecycleScope.launch { addCount(+1,true) }}
override fun onSubtractedOne(questType: String) { viewLifecycleScope.launch { addCount(-1,true) }}
override fun onReplacedAll() { viewLifecycleScope.launch { updateCount(false) }}
}

/* --------------------------------------- Lifecycle ---------------------------------------- */
Expand All @@ -51,7 +51,7 @@ class AnswersCounterFragment : Fragment(R.layout.fragment_answers_counter) {
* upload button, and shows the uploaded + uploadable amount of quests.
*/
updateProgress(uploadProgressSource.isUploadInProgress)
lifecycleScope.launch { updateCount(false) }
viewLifecycleScope.launch { updateCount(false) }
if (isAutosync) {
uploadProgressSource.addUploadProgressListener(uploadProgressListener)
unsyncedChangesCountSource.addListener(unsyncedChangesCountListener)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ package de.westnordost.streetcomplete.controls
import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import de.westnordost.streetcomplete.Injector
import de.westnordost.streetcomplete.R
import de.westnordost.streetcomplete.data.download.DownloadProgressListener
import de.westnordost.streetcomplete.data.download.DownloadProgressSource
import de.westnordost.streetcomplete.ktx.toPx
import de.westnordost.streetcomplete.ktx.viewLifecycleScope
import kotlinx.coroutines.launch
import javax.inject.Inject

Expand All @@ -20,8 +20,8 @@ class DownloadProgressFragment : Fragment(R.layout.fragment_download_progress) {
private val progressView get() = view as IconsDownloadProgressView

private val downloadProgressListener = object : DownloadProgressListener {
override fun onStarted() { lifecycleScope.launch { animateInProgressView() }}
override fun onFinished() { lifecycleScope.launch { animateOutProgressView() }}
override fun onStarted() { viewLifecycleScope.launch { animateInProgressView() }}
override fun onFinished() { viewLifecycleScope.launch { animateOutProgressView() }}
}

init {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,13 @@ import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.core.content.getSystemService
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import de.westnordost.streetcomplete.Injector
import de.westnordost.streetcomplete.R
import de.westnordost.streetcomplete.data.download.DownloadController
import de.westnordost.streetcomplete.data.osm.mapdata.BoundingBox
import de.westnordost.streetcomplete.data.visiblequests.TeamModeQuestFilter
import de.westnordost.streetcomplete.databinding.FragmentMainMenuButtonBinding
import de.westnordost.streetcomplete.ktx.popIn
import de.westnordost.streetcomplete.ktx.popOut
import de.westnordost.streetcomplete.ktx.toast
import de.westnordost.streetcomplete.ktx.viewBinding
import de.westnordost.streetcomplete.ktx.*
import kotlinx.coroutines.launch
import javax.inject.Inject

Expand All @@ -35,7 +31,7 @@ class MainMenuButtonFragment : Fragment(R.layout.fragment_main_menu_button) {
private val binding by viewBinding(FragmentMainMenuButtonBinding::bind)

private val teamModeListener = object : TeamModeQuestFilter.TeamModeChangeListener {
override fun onTeamModeChanged(enabled: Boolean) { lifecycleScope.launch { setTeamMode(enabled) } }
override fun onTeamModeChanged(enabled: Boolean) { viewLifecycleScope.launch { setTeamMode(enabled) } }
}

/* --------------------------------------- Lifecycle ---------------------------------------- */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import android.view.View
import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import de.westnordost.streetcomplete.Injector
import de.westnordost.streetcomplete.R
import de.westnordost.streetcomplete.data.notifications.*
import de.westnordost.streetcomplete.ktx.popIn
import de.westnordost.streetcomplete.ktx.popOut
import de.westnordost.streetcomplete.ktx.viewLifecycleScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
Expand All @@ -30,7 +30,7 @@ class NotificationButtonFragment : Fragment(R.layout.fragment_notification_butto

private var notificationsSourceUpdateListener = object : NotificationsSource.UpdateListener {
override fun onNumberOfNotificationsUpdated(numberOfNotifications: Int) {
lifecycleScope.launch { updateButtonStateAnimated(numberOfNotifications) }
viewLifecycleScope.launch { updateButtonStateAnimated(numberOfNotifications) }
}
}

Expand All @@ -40,13 +40,13 @@ class NotificationButtonFragment : Fragment(R.layout.fragment_notification_butto

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
notificationButton.setOnClickListener { lifecycleScope.launch { onClickButton() } }
notificationButton.setOnClickListener { viewLifecycleScope.launch { onClickButton() } }
}

override fun onStart() {
super.onStart()
notificationsSource.addListener(notificationsSourceUpdateListener)
lifecycleScope.launch { initializeButtonState() }
viewLifecycleScope.launch { initializeButtonState() }
}

override fun onStop() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import android.widget.PopupMenu
import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import de.westnordost.streetcomplete.Injector
import de.westnordost.streetcomplete.R
import de.westnordost.streetcomplete.data.edithistory.Edit
Expand All @@ -18,6 +17,7 @@ import de.westnordost.streetcomplete.data.upload.UploadProgressSource
import de.westnordost.streetcomplete.edithistory.UndoDialog
import de.westnordost.streetcomplete.ktx.popIn
import de.westnordost.streetcomplete.ktx.popOut
import de.westnordost.streetcomplete.ktx.viewLifecycleScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
Expand All @@ -38,17 +38,17 @@ class UndoButtonFragment : Fragment(R.layout.fragment_undo_button) {

/* undo button is not shown when there is nothing to undo */
private val editHistoryListener = object : EditHistorySource.Listener {
override fun onAdded(edit: Edit) { lifecycleScope.launch { animateInIfAnythingToUndo() }}
override fun onSynced(edit: Edit) { lifecycleScope.launch { animateOutIfNothingLeftToUndo() }}
override fun onDeleted(edits: List<Edit>) { lifecycleScope.launch { animateOutIfNothingLeftToUndo() }}
override fun onInvalidated() { lifecycleScope.launch { updateUndoButtonVisibility() }}
override fun onAdded(edit: Edit) { viewLifecycleScope.launch { animateInIfAnythingToUndo() }}
override fun onSynced(edit: Edit) { viewLifecycleScope.launch { animateOutIfNothingLeftToUndo() }}
override fun onDeleted(edits: List<Edit>) { viewLifecycleScope.launch { animateOutIfNothingLeftToUndo() }}
override fun onInvalidated() { viewLifecycleScope.launch { updateUndoButtonVisibility() }}
}

/* Don't allow undoing while uploading. Should prevent race conditions. (Undoing quest while
* also uploading it at the same time) */
private val uploadProgressListener = object : UploadProgressListener {
override fun onStarted() { lifecycleScope.launch { updateUndoButtonEnablement(false) }}
override fun onFinished() { lifecycleScope.launch { updateUndoButtonEnablement(true) }}
override fun onStarted() { viewLifecycleScope.launch { updateUndoButtonEnablement(false) }}
override fun onFinished() { viewLifecycleScope.launch { updateUndoButtonEnablement(true) }}
}

/* --------------------------------------- Lifecycle ---------------------------------------- */
Expand All @@ -67,7 +67,7 @@ class UndoButtonFragment : Fragment(R.layout.fragment_undo_button) {

override fun onStart() {
super.onStart()
lifecycleScope.launch { updateUndoButtonVisibility() }
viewLifecycleScope.launch { updateUndoButtonVisibility() }
updateUndoButtonEnablement(true)
editHistorySource.addListener(editHistoryListener)
uploadProgressSource.addUploadProgressListener(uploadProgressListener)
Expand All @@ -92,7 +92,7 @@ class UndoButtonFragment : Fragment(R.layout.fragment_undo_button) {

popup.setOnMenuItemClickListener { item ->
when(item.itemId) {
undo -> lifecycleScope.launch { confirmUndo() }
undo -> viewLifecycleScope.launch { confirmUndo() }
showHistory -> showEditHistory()
}
true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import android.view.View
import androidx.core.content.getSystemService
import androidx.core.view.isGone
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import de.westnordost.streetcomplete.Injector
import de.westnordost.streetcomplete.Prefs
import de.westnordost.streetcomplete.R
Expand All @@ -16,6 +15,7 @@ import de.westnordost.streetcomplete.data.upload.UploadController
import de.westnordost.streetcomplete.data.upload.UploadProgressListener
import de.westnordost.streetcomplete.data.user.UserController
import de.westnordost.streetcomplete.ktx.toast
import de.westnordost.streetcomplete.ktx.viewLifecycleScope
import de.westnordost.streetcomplete.view.dialogs.RequestLoginDialog
import kotlinx.coroutines.launch
import javax.inject.Inject
Expand All @@ -28,16 +28,16 @@ class UploadButtonFragment : Fragment(R.layout.fragment_upload_button) {
@Inject internal lateinit var unsyncedChangesCountSource: UnsyncedChangesCountSource
@Inject internal lateinit var prefs: SharedPreferences

private val uploadButton get() = view as? UploadButton
private val uploadButton get() = view as UploadButton

private val unsyncedChangesCountListener = object : UnsyncedChangesCountSource.Listener {
override fun onIncreased() { lifecycleScope.launch { updateCount() }}
override fun onDecreased() { lifecycleScope.launch { updateCount() }}
override fun onIncreased() { viewLifecycleScope.launch { updateCount() }}
override fun onDecreased() { viewLifecycleScope.launch { updateCount() }}
}

private val uploadProgressListener = object : UploadProgressListener {
override fun onStarted() { lifecycleScope.launch { updateProgress(true) } }
override fun onFinished() { lifecycleScope.launch { updateProgress(false) } }
override fun onStarted() { viewLifecycleScope.launch { updateProgress(true) } }
override fun onFinished() { viewLifecycleScope.launch { updateProgress(false) } }
}

/* --------------------------------------- Lifecycle ---------------------------------------- */
Expand All @@ -49,7 +49,7 @@ class UploadButtonFragment : Fragment(R.layout.fragment_upload_button) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

uploadButton?.setOnClickListener {
uploadButton.setOnClickListener {
if (isConnected()) {
uploadChanges()
} else {
Expand All @@ -61,9 +61,9 @@ class UploadButtonFragment : Fragment(R.layout.fragment_upload_button) {
override fun onStart() {
super.onStart()
/* Only show the button if autosync is off */
uploadButton?.isGone = isAutosync
uploadButton.isGone = isAutosync
if (!isAutosync) {
lifecycleScope.launch { updateCount() }
viewLifecycleScope.launch { updateCount() }
updateProgress(uploadController.isUploadInProgress)
unsyncedChangesCountSource.addListener(unsyncedChangesCountListener)
uploadController.addUploadProgressListener(uploadProgressListener)
Expand All @@ -82,16 +82,16 @@ class UploadButtonFragment : Fragment(R.layout.fragment_upload_button) {
Prefs.Autosync.valueOf(prefs.getString(Prefs.AUTOSYNC, "ON")!!) == Prefs.Autosync.ON

private suspend fun updateCount() {
uploadButton?.uploadableCount = unsyncedChangesCountSource.getCount()
uploadButton.uploadableCount = unsyncedChangesCountSource.getCount()
}

private fun updateProgress(isUploadInProgress: Boolean) {
if (isUploadInProgress) {
uploadButton?.isEnabled = false
uploadButton?.showProgress = true
uploadButton.isEnabled = false
uploadButton.showProgress = true
} else {
uploadButton?.isEnabled = true
uploadButton?.showProgress = false
uploadButton.isEnabled = true
uploadButton.showProgress = false
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import de.westnordost.streetcomplete.data.user.UserLoginStatusListener
import de.westnordost.streetcomplete.data.visiblequests.TeamModeQuestFilter
import de.westnordost.streetcomplete.ktx.format
import de.westnordost.streetcomplete.location.FineLocationManager
import kotlinx.coroutines.*
import javax.inject.Inject
import javax.inject.Singleton

Expand Down Expand Up @@ -49,8 +48,6 @@ import javax.inject.Singleton
private var isConnected: Boolean = false
private var isWifi: Boolean = false

private val lifecycleScope = CoroutineScope(SupervisorJob())

// new location is known -> check if downloading makes sense now
private val locationManager = FineLocationManager(context.getSystemService<LocationManager>()!!) { location ->
if (location.accuracy <= 300) {
Expand Down Expand Up @@ -137,7 +134,6 @@ import javax.inject.Singleton
downloadProgressSource.removeDownloadProgressListener(downloadProgressListener)
loginStatusSource.removeLoginStatusListener(userLoginStatusListener)
teamModeQuestFilter.removeListener(teamModeChangeListener)
lifecycleScope.cancel()
}

@SuppressLint("MissingPermission")
Expand All @@ -158,17 +154,15 @@ import javax.inject.Singleton

Log.i(TAG, "Checking whether to automatically download new quests at ${pos.latitude.format(7)},${pos.longitude.format(7)}")

lifecycleScope.launch {
val downloadStrategy = if (isWifi) wifiDownloadStrategy else mobileDataDownloadStrategy
val downloadBoundingBox = downloadStrategy.getDownloadBoundingBox(pos)
if (downloadBoundingBox != null) {
try {
downloadController.download(downloadBoundingBox)
} catch (e: IllegalStateException) {
// The Android 9 bug described here should not result in a hard crash of the app
// https://stackoverflow.com/questions/52013545/android-9-0-not-allowed-to-start-service-app-is-in-background-after-onresume
Log.e(TAG, "Cannot start download service", e)
}
val downloadStrategy = if (isWifi) wifiDownloadStrategy else mobileDataDownloadStrategy
val downloadBoundingBox = downloadStrategy.getDownloadBoundingBox(pos)
if (downloadBoundingBox != null) {
try {
downloadController.download(downloadBoundingBox)
} catch (e: IllegalStateException) {
// The Android 9 bug described here should not result in a hard crash of the app
// https://stackoverflow.com/questions/52013545/android-9-0-not-allowed-to-start-service-app-is-in-background-after-onresume
Log.e(TAG, "Cannot start download service", e)
}
}
}
Expand Down
Loading

0 comments on commit d677942

Please sign in to comment.