From cc2a2b9257fa0d88726566559aa14cea01528cfc Mon Sep 17 00:00:00 2001 From: Tobias Zwick Date: Mon, 4 Oct 2021 14:48:19 +0200 Subject: [PATCH] let the async code in fragments heed the fragment's view lifecycle , ... not the general lifecycle. Unfortunately, there is a difference. I.e. the view is removed before the fragment is destroyed and can (theoretically?) also be recreated without the fragment being recreated. Since basically any code in fragments is view-related, this is almost a search&replace --- .../streetcomplete/about/ChangelogFragment.kt | 4 +-- .../streetcomplete/about/CreditsFragment.kt | 4 +-- .../controls/AnswersCounterFragment.kt | 18 ++++++------ .../controls/DownloadProgressFragment.kt | 6 ++-- .../controls/MainMenuButtonFragment.kt | 8 ++---- .../controls/NotificationButtonFragment.kt | 8 +++--- .../controls/UndoButtonFragment.kt | 18 ++++++------ .../controls/UploadButtonFragment.kt | 28 +++++++++---------- .../data/quest/QuestAutoSyncer.kt | 24 ++++++---------- .../visiblequests/QuestPresetsController.kt | 2 +- .../edithistory/EditHistoryFragment.kt | 12 ++++---- .../streetcomplete/ktx/Fragment.kt | 5 +++- .../map/EditHistoryPinsManager.kt | 16 ++++------- .../map/LocationAwareMapFragment.kt | 4 +-- .../streetcomplete/map/MainFragment.kt | 27 +++++++++--------- .../streetcomplete/map/MapFragment.kt | 13 +++------ .../streetcomplete/map/QuestPinsManager.kt | 18 +++++------- .../streetcomplete/map/QuestsMapFragment.kt | 26 ++++++++--------- .../map/tangram/KtMapController.kt | 10 +++---- .../OsmUnreadMessagesFragment.kt | 4 +-- .../quests/AbstractBottomSheetFragment.kt | 4 +-- .../streetcomplete/quests/SplitWayFragment.kt | 5 ++-- .../quests/lanes/AddLanesForm.kt | 8 +++--- .../oneway_suspects/AddSuspectedOnewayForm.kt | 6 ++-- .../streetcomplete/settings/OAuthFragment.kt | 4 +-- .../settings/SettingsFragment.kt | 6 ++-- .../questselection/QuestPresetsAdapter.kt | 14 +++++----- .../questselection/QuestPresetsFragment.kt | 10 +++---- .../questselection/QuestSelectionAdapter.kt | 17 +++++------ .../questselection/QuestSelectionFragment.kt | 12 ++++---- .../tutorial/TutorialFragment.kt | 6 ++-- .../user/AchievementsFragment.kt | 4 +-- .../streetcomplete/user/BallPitView.kt | 6 ++-- .../streetcomplete/user/LinksFragment.kt | 8 ++---- .../streetcomplete/user/LoginFragment.kt | 6 ++-- .../streetcomplete/user/ProfileFragment.kt | 18 ++++++------ .../user/QuestStatisticsByCountryFragment.kt | 4 +-- .../QuestStatisticsByQuestTypeFragment.kt | 4 +-- .../user/QuestStatisticsFragment.kt | 4 +-- .../QuestPresetControllerTest.kt | 2 +- 40 files changed, 186 insertions(+), 217 deletions(-) diff --git a/app/src/main/java/de/westnordost/streetcomplete/about/ChangelogFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/about/ChangelogFragment.kt index 62a1a4f411..7889d0fdb1 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/about/ChangelogFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/about/ChangelogFragment.kt @@ -9,7 +9,6 @@ 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 @@ -17,6 +16,7 @@ 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.* @@ -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) } diff --git a/app/src/main/java/de/westnordost/streetcomplete/about/CreditsFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/about/CreditsFragment.kt index 00968263f6..7f57cb5203 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/about/CreditsFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/about/CreditsFragment.kt @@ -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 @@ -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) diff --git a/app/src/main/java/de/westnordost/streetcomplete/controls/AnswersCounterFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/controls/AnswersCounterFragment.kt index 5bbc7cf9a1..f509f14f43 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/controls/AnswersCounterFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/controls/AnswersCounterFragment.kt @@ -2,7 +2,6 @@ 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 @@ -10,6 +9,7 @@ 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 @@ -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 ---------------------------------------- */ @@ -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) diff --git a/app/src/main/java/de/westnordost/streetcomplete/controls/DownloadProgressFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/controls/DownloadProgressFragment.kt index f26ba860bf..63c4b4e526 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/controls/DownloadProgressFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/controls/DownloadProgressFragment.kt @@ -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 @@ -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 { diff --git a/app/src/main/java/de/westnordost/streetcomplete/controls/MainMenuButtonFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/controls/MainMenuButtonFragment.kt index a7e129ec7d..2fdc442672 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/controls/MainMenuButtonFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/controls/MainMenuButtonFragment.kt @@ -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 @@ -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 ---------------------------------------- */ diff --git a/app/src/main/java/de/westnordost/streetcomplete/controls/NotificationButtonFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/controls/NotificationButtonFragment.kt index 60b42d6cbd..7304813166 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/controls/NotificationButtonFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/controls/NotificationButtonFragment.kt @@ -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 @@ -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) } } } @@ -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() { diff --git a/app/src/main/java/de/westnordost/streetcomplete/controls/UndoButtonFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/controls/UndoButtonFragment.kt index 0ea8d2219a..f9f512435b 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/controls/UndoButtonFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/controls/UndoButtonFragment.kt @@ -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 @@ -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 @@ -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) { 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) { 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 ---------------------------------------- */ @@ -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) @@ -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 diff --git a/app/src/main/java/de/westnordost/streetcomplete/controls/UploadButtonFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/controls/UploadButtonFragment.kt index 377de9b3a2..49f859d965 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/controls/UploadButtonFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/controls/UploadButtonFragment.kt @@ -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 @@ -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 @@ -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 ---------------------------------------- */ @@ -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 { @@ -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) @@ -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 } } diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/quest/QuestAutoSyncer.kt b/app/src/main/java/de/westnordost/streetcomplete/data/quest/QuestAutoSyncer.kt index 3723d114be..fd28e2dece 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/quest/QuestAutoSyncer.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/quest/QuestAutoSyncer.kt @@ -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 @@ -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()!!) { location -> if (location.accuracy <= 300) { @@ -137,7 +134,6 @@ import javax.inject.Singleton downloadProgressSource.removeDownloadProgressListener(downloadProgressListener) loginStatusSource.removeLoginStatusListener(userLoginStatusListener) teamModeQuestFilter.removeListener(teamModeChangeListener) - lifecycleScope.cancel() } @SuppressLint("MissingPermission") @@ -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) } } } diff --git a/app/src/main/java/de/westnordost/streetcomplete/data/visiblequests/QuestPresetsController.kt b/app/src/main/java/de/westnordost/streetcomplete/data/visiblequests/QuestPresetsController.kt index 1148e631c0..b86733cb62 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/data/visiblequests/QuestPresetsController.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/data/visiblequests/QuestPresetsController.kt @@ -21,7 +21,7 @@ import javax.inject.Singleton override val selectedQuestPresetName: String? get() = questPresetsDao.getName(selectedQuestPresetId) - fun addQuestProfile(presetName: String): Long { + fun addQuestPreset(presetName: String): Long { val presetId = questPresetsDao.add(presetName) onAddedQuestProfile(presetId, presetName) return presetId diff --git a/app/src/main/java/de/westnordost/streetcomplete/edithistory/EditHistoryFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/edithistory/EditHistoryFragment.kt index 317322e31f..54096a4a8c 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/edithistory/EditHistoryFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/edithistory/EditHistoryFragment.kt @@ -4,7 +4,6 @@ import android.os.Bundle import android.view.View import androidx.core.view.updatePadding 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 @@ -12,6 +11,7 @@ import de.westnordost.streetcomplete.data.edithistory.EditHistorySource import de.westnordost.streetcomplete.data.edithistory.EditKey import de.westnordost.streetcomplete.databinding.FragmentEditHistoryListBinding import de.westnordost.streetcomplete.ktx.viewBinding +import de.westnordost.streetcomplete.ktx.viewLifecycleScope import de.westnordost.streetcomplete.view.insets_animation.respectSystemInsets import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -38,11 +38,11 @@ class EditHistoryFragment : Fragment(R.layout.fragment_edit_history_list) { private val adapter = EditHistoryAdapter(this::onSelected, this::onSelectionDeleted, this::onUndo) private val editHistoryListener = object : EditHistorySource.Listener { - override fun onAdded(edit: Edit) { lifecycleScope.launch { adapter.onAdded(edit) } } - override fun onSynced(edit: Edit) { lifecycleScope.launch { adapter.onSynced(edit) } } + override fun onAdded(edit: Edit) { viewLifecycleScope.launch { adapter.onAdded(edit) } } + override fun onSynced(edit: Edit) { viewLifecycleScope.launch { adapter.onSynced(edit) } } override fun onDeleted(edits: List) { - lifecycleScope.launch { + viewLifecycleScope.launch { adapter.onDeleted(edits) if (editHistorySource.getCount() == 0) { listener?.onEditHistoryIsEmpty() @@ -51,7 +51,7 @@ class EditHistoryFragment : Fragment(R.layout.fragment_edit_history_list) { } override fun onInvalidated() { - lifecycleScope.launch { + viewLifecycleScope.launch { val edits = withContext(Dispatchers.IO) { editHistorySource.getAll() } adapter.setEdits(edits) if (edits.isEmpty()) { @@ -74,7 +74,7 @@ class EditHistoryFragment : Fragment(R.layout.fragment_edit_history_list) { super.onViewCreated(view, savedInstanceState) binding.editHistoryList.respectSystemInsets { updatePadding(left = it.left, top = it.top, bottom = it.bottom) } - lifecycleScope.launch { + viewLifecycleScope.launch { val edits = withContext(Dispatchers.IO) { editHistorySource.getAll() } adapter.setEdits(edits) binding.editHistoryList.adapter = adapter diff --git a/app/src/main/java/de/westnordost/streetcomplete/ktx/Fragment.kt b/app/src/main/java/de/westnordost/streetcomplete/ktx/Fragment.kt index ac6287891f..597f728a44 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/ktx/Fragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/ktx/Fragment.kt @@ -4,6 +4,7 @@ import android.content.ActivityNotFoundException import android.content.Intent import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager +import androidx.lifecycle.lifecycleScope fun Fragment.tryStartActivity(intent: Intent): Boolean { return try { @@ -15,4 +16,6 @@ fun Fragment.tryStartActivity(intent: Intent): Boolean { } val Fragment.childFragmentManagerOrNull: FragmentManager? get() = - if (host != null) childFragmentManager else null \ No newline at end of file + if (host != null) childFragmentManager else null + +val Fragment.viewLifecycleScope get() = viewLifecycleOwner.lifecycleScope diff --git a/app/src/main/java/de/westnordost/streetcomplete/map/EditHistoryPinsManager.kt b/app/src/main/java/de/westnordost/streetcomplete/map/EditHistoryPinsManager.kt index 1db8308fdc..d0e4c83ae5 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/map/EditHistoryPinsManager.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/map/EditHistoryPinsManager.kt @@ -23,14 +23,14 @@ class EditHistoryPinsManager( ): LifecycleObserver { /** Switch visibility of edit history pins layer */ - var isActive: Boolean = false + var isVisible: Boolean = false set(value) { if (field == value) return field = value if (value) show() else hide() } - private val lifecycleScope: CoroutineScope + private val viewLifecycleScope: CoroutineScope = CoroutineScope(SupervisorJob()) private val editHistoryListener = object : EditHistorySource.Listener { override fun onAdded(edit: Edit) { updatePins() } @@ -39,13 +39,9 @@ class EditHistoryPinsManager( override fun onInvalidated() { updatePins() } } - init { - lifecycleScope = CoroutineScope(SupervisorJob()) - } - @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) fun onDestroy() { hide() - lifecycleScope.cancel() + viewLifecycleScope.cancel() } private fun show() { @@ -55,7 +51,7 @@ class EditHistoryPinsManager( private fun hide() { pinsMapComponent.clear() - lifecycleScope.coroutineContext.cancelChildren() + viewLifecycleScope.coroutineContext.cancelChildren() editHistorySource.removeListener(editHistoryListener) } @@ -63,8 +59,8 @@ class EditHistoryPinsManager( properties.toEditKey() private fun updatePins() { - lifecycleScope.launch { - if (this@EditHistoryPinsManager.isActive) { + viewLifecycleScope.launch { + if (isVisible) { val edits = withContext(Dispatchers.IO) { editHistorySource.getAll() } pinsMapComponent.showPins(createEditPins(edits)) } diff --git a/app/src/main/java/de/westnordost/streetcomplete/map/LocationAwareMapFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/map/LocationAwareMapFragment.kt index d5a31284aa..d3137bec8c 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/map/LocationAwareMapFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/map/LocationAwareMapFragment.kt @@ -10,8 +10,8 @@ import android.os.Bundle import android.view.WindowManager import androidx.core.content.edit import androidx.core.content.getSystemService -import androidx.lifecycle.lifecycleScope import de.westnordost.streetcomplete.data.osm.mapdata.LatLon +import de.westnordost.streetcomplete.ktx.viewLifecycleScope import de.westnordost.streetcomplete.location.FineLocationManager import de.westnordost.streetcomplete.location.toLatLon import de.westnordost.streetcomplete.map.components.CurrentLocationMapComponent @@ -210,7 +210,7 @@ open class LocationAwareMapFragment : MapFragment() { tracks.last().add(location) // delay update by 600 ms because the animation to the new location takes that long - lifecycleScope.launch { + viewLifecycleScope.launch { delay(600) tracksMapComponent?.addToCurrentTrack(location) } diff --git a/app/src/main/java/de/westnordost/streetcomplete/map/MainFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/map/MainFragment.kt index 509ed32a3e..5606502a02 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/map/MainFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/map/MainFragment.kt @@ -29,7 +29,6 @@ import androidx.core.view.isInvisible import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager.POP_BACK_STACK_INCLUSIVE import androidx.fragment.app.commit -import androidx.lifecycle.lifecycleScope import androidx.localbroadcastmanager.content.LocalBroadcastManager import de.westnordost.streetcomplete.* import de.westnordost.streetcomplete.controls.MainMenuButtonFragment @@ -291,9 +290,9 @@ class MainFragment : Fragment(R.layout.fragment_main), if (isQuestDetailsCurrentlyDisplayedFor(questKey)) return val f = bottomSheetFragment if (f is IsCloseableBottomSheet) f.onClickClose { - lifecycleScope.launch { showQuestDetails(questKey) } + viewLifecycleScope.launch { showQuestDetails(questKey) } } - else lifecycleScope.launch { showQuestDetails(questKey) } + else viewLifecycleScope.launch { showQuestDetails(questKey) } } override fun onClickedEdit(editKey: EditKey) { @@ -356,7 +355,7 @@ class MainFragment : Fragment(R.layout.fragment_main), /* -------------------------- AbstractQuestAnswerFragment.Listener -------------------------- */ override fun onAnsweredQuest(questKey: QuestKey, answer: Any) { - lifecycleScope.launch { + viewLifecycleScope.launch { solveQuest(questKey) { quest -> if (questController.solve(quest, answer, "survey")) { onQuestSolved(quest, "survey") @@ -370,7 +369,7 @@ class MainFragment : Fragment(R.layout.fragment_main), } override fun onSplitWay(osmQuestKey: OsmQuestKey) { - lifecycleScope.launch { + viewLifecycleScope.launch { val quest = questController.get(osmQuestKey)!! val element = questController.getOsmElement(quest as OsmQuest) val geometry = quest.geometry @@ -383,14 +382,14 @@ class MainFragment : Fragment(R.layout.fragment_main), } override fun onSkippedQuest(questKey: QuestKey) { - lifecycleScope.launch { + viewLifecycleScope.launch { closeBottomSheet() questController.hide(questKey) } } override fun onDeletePoiNode(osmQuestKey: OsmQuestKey) { - lifecycleScope.launch { + viewLifecycleScope.launch { solveQuest(osmQuestKey) { quest -> if (questController.deletePoiElement(quest as OsmQuest, "survey")) { onQuestSolved(quest, "survey") @@ -400,7 +399,7 @@ class MainFragment : Fragment(R.layout.fragment_main), } override fun onReplaceShopElement(osmQuestKey: OsmQuestKey, tags: Map) { - lifecycleScope.launch { + viewLifecycleScope.launch { solveQuest(osmQuestKey) { quest -> if (questController.replaceShopElement(quest as OsmQuest, tags, "survey")) { onQuestSolved(quest, "survey") @@ -432,7 +431,7 @@ class MainFragment : Fragment(R.layout.fragment_main), /* ------------------------------- SplitWayFragment.Listener -------------------------------- */ override fun onSplittedWay(osmQuestKey: OsmQuestKey, splits: List) { - lifecycleScope.launch { + viewLifecycleScope.launch { solveQuest(osmQuestKey) { quest -> if (questController.splitWay(quest as OsmQuest, splits, "survey")) { onQuestSolved(quest, "survey") @@ -453,7 +452,7 @@ class MainFragment : Fragment(R.layout.fragment_main), override fun onCreatedNoteInstead(questKey: QuestKey, questTitle: String, note: String, imagePaths: List) { // the quest is deleted from DB on creating a note, so need to fetch quest before - lifecycleScope.launch { + viewLifecycleScope.launch { val quest = questController.get(questKey) if (quest != null) { closeBottomSheet() @@ -482,7 +481,7 @@ class MainFragment : Fragment(R.layout.fragment_main), */ closeBottomSheet() - lifecycleScope.launch { questController.createNote(note, imagePaths, position) } + viewLifecycleScope.launch { questController.createNote(note, imagePaths, position) } listener?.onCreatedNote(screenPosition) showMarkerSolvedAnimation(R.drawable.ic_quest_create_note, PointF(screenPosition)) @@ -495,7 +494,7 @@ class MainFragment : Fragment(R.layout.fragment_main), /* ---------------------------------- VisibleQuestListener ---------------------------------- */ @AnyThread override fun onUpdatedVisibleQuests(added: Collection, removed: Collection) { - lifecycleScope.launch { + viewLifecycleScope.launch { val f = bottomSheetFragment if (f !is IsShowingQuestDetails) return@launch @@ -507,7 +506,7 @@ class MainFragment : Fragment(R.layout.fragment_main), } @AnyThread override fun onVisibleQuestsInvalidated() { - lifecycleScope.launch { + viewLifecycleScope.launch { val f = bottomSheetFragment if (f !is IsShowingQuestDetails) return@launch @@ -874,7 +873,7 @@ class MainFragment : Fragment(R.layout.fragment_main), val activity = activity ?: return val view = view ?: return - lifecycleScope.launch { + viewLifecycleScope.launch { soundFx.play(resources.getIdentifier("plop" + Random.nextInt(4), "raw", ctx.packageName)) } diff --git a/app/src/main/java/de/westnordost/streetcomplete/map/MapFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/map/MapFragment.kt index 60adf1ef46..6eda313e44 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/map/MapFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/map/MapFragment.kt @@ -16,7 +16,6 @@ import androidx.appcompat.app.AlertDialog import androidx.core.content.edit import androidx.core.net.toUri import androidx.fragment.app.Fragment -import androidx.lifecycle.lifecycleScope import com.mapzen.tangram.MapView import com.mapzen.tangram.TouchInput.* import com.mapzen.tangram.networking.DefaultHttpHandler @@ -29,11 +28,7 @@ import de.westnordost.streetcomplete.data.maptiles.MapTilesDownloadCacheConfig import de.westnordost.streetcomplete.data.osm.mapdata.BoundingBox import de.westnordost.streetcomplete.data.osm.mapdata.LatLon import de.westnordost.streetcomplete.databinding.FragmentMapBinding -import de.westnordost.streetcomplete.ktx.awaitLayout -import de.westnordost.streetcomplete.ktx.containsAll -import de.westnordost.streetcomplete.ktx.setMargins -import de.westnordost.streetcomplete.ktx.tryStartActivity -import de.westnordost.streetcomplete.ktx.viewBinding +import de.westnordost.streetcomplete.ktx.* import de.westnordost.streetcomplete.map.components.SceneMapComponent import de.westnordost.streetcomplete.map.tangram.* import de.westnordost.streetcomplete.view.insets_animation.respectSystemInsets @@ -71,7 +66,7 @@ open class MapFragment : Fragment(), val toggle = if (value) "true" else "false" - lifecycleScope.launch { + viewLifecycleScope.launch { sceneMapComponent?.putSceneUpdates(listOf( "layers.buildings.draw.buildings-style.extrude" to toggle, "layers.buildings.draw.buildings-outline-style.extrude" to toggle @@ -125,7 +120,7 @@ open class MapFragment : Fragment(), binding.attributionContainer.respectSystemInsets(View::setMargins) - lifecycleScope.launch { initMap() } + viewLifecycleScope.launch { initMap() } } private fun showOpenUrlDialog(url: String) { @@ -152,7 +147,7 @@ open class MapFragment : Fragment(), override fun onStart() { super.onStart() - lifecycleScope.launch { sceneMapComponent?.loadScene() } + viewLifecycleScope.launch { sceneMapComponent?.loadScene() } } override fun onResume() { diff --git a/app/src/main/java/de/westnordost/streetcomplete/map/QuestPinsManager.kt b/app/src/main/java/de/westnordost/streetcomplete/map/QuestPinsManager.kt index d8a6303b79..8ba228f6f5 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/map/QuestPinsManager.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/map/QuestPinsManager.kt @@ -36,10 +36,10 @@ class QuestPinsManager( // quest key -> [point, ...] private val quests: MutableMap> = mutableMapOf() - private val lifecycleScope: CoroutineScope + private val viewLifecycleScope: CoroutineScope = CoroutineScope(SupervisorJob()) /** Switch visibility of quest pins layer */ - var isActive: Boolean = false + var isVisible: Boolean = false set(value) { if (field == value) return field = value @@ -69,13 +69,9 @@ class QuestPinsManager( } } - init { - lifecycleScope = CoroutineScope(SupervisorJob()) - } - @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) fun onDestroy() { hide() - lifecycleScope.cancel() + viewLifecycleScope.cancel() } private fun show() { @@ -87,7 +83,7 @@ class QuestPinsManager( private fun hide() { clear() - lifecycleScope.coroutineContext.cancelChildren() + viewLifecycleScope.coroutineContext.cancelChildren() visibleQuestsSource.removeListener(visibleQuestsListener) questTypeOrderSource.removeListener(questTypeOrderListener) } @@ -101,7 +97,7 @@ class QuestPinsManager( properties.toQuestKey() fun onNewScreenPosition() { - if (!isActive) return + if (!isVisible) return val zoom = ctrl.cameraPosition.zoom if (zoom < TILES_ZOOM) return val displayedArea = ctrl.screenAreaToBoundingBox(RectF()) ?: return @@ -123,7 +119,7 @@ class QuestPinsManager( } val minRect = tiles.minTileRect() ?: return val bbox = minRect.asBoundingBox(TILES_ZOOM) - lifecycleScope.launch { + viewLifecycleScope.launch { val quests = withContext(Dispatchers.IO) { visibleQuestsSource.getAllVisible(bbox) } var addedAny = false for (quest in quests) { @@ -164,7 +160,7 @@ class QuestPinsManager( } private fun updatePins() { - if (isActive) { + if (isVisible) { val pins = synchronized(quests) { quests.values.flatten() } pinsMapComponent.showPins(pins) } diff --git a/app/src/main/java/de/westnordost/streetcomplete/map/QuestsMapFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/map/QuestsMapFragment.kt index 42d75054ea..7c9c64ff2d 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/map/QuestsMapFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/map/QuestsMapFragment.kt @@ -3,7 +3,6 @@ package de.westnordost.streetcomplete.map import android.graphics.PointF import android.graphics.RectF import androidx.annotation.DrawableRes -import androidx.lifecycle.lifecycleScope import de.westnordost.streetcomplete.Injector import de.westnordost.streetcomplete.data.edithistory.Edit import de.westnordost.streetcomplete.data.edithistory.EditHistorySource @@ -21,6 +20,7 @@ import de.westnordost.streetcomplete.data.quest.QuestTypeRegistry import de.westnordost.streetcomplete.data.quest.VisibleQuestsSource import de.westnordost.streetcomplete.data.visiblequests.QuestTypeOrderSource import de.westnordost.streetcomplete.ktx.toPx +import de.westnordost.streetcomplete.ktx.viewLifecycleScope import de.westnordost.streetcomplete.map.components.ElementGeometryMapComponent import de.westnordost.streetcomplete.map.components.PinsMapComponent import de.westnordost.streetcomplete.map.components.PointMarkersMapComponent @@ -76,12 +76,12 @@ class QuestsMapFragment : LocationAwareMapFragment() { geometryMapComponent = ElementGeometryMapComponent(ctrl) questPinsManager = QuestPinsManager(ctrl, pinsMapComponent!!, questTypeOrderSource, questTypeRegistry, resources, visibleQuestsSource) - lifecycle.addObserver(questPinsManager!!) - questPinsManager!!.isActive = pinMode == PinMode.QUESTS + viewLifecycleOwner.lifecycle.addObserver(questPinsManager!!) + questPinsManager!!.isVisible = pinMode == PinMode.QUESTS editHistoryPinsManager = EditHistoryPinsManager(pinsMapComponent!!, editHistorySource, resources) - lifecycle.addObserver(editHistoryPinsManager!!) - editHistoryPinsManager!!.isActive = pinMode == PinMode.EDITS + viewLifecycleOwner.lifecycle.addObserver(editHistoryPinsManager!!) + editHistoryPinsManager!!.isVisible = pinMode == PinMode.EDITS super.onMapReady() } @@ -102,7 +102,7 @@ class QuestsMapFragment : LocationAwareMapFragment() { /* -------------------------------- Picking quest pins -------------------------------------- */ override fun onSingleTapConfirmed(x: Float, y: Float): Boolean { - lifecycleScope.launch { + viewLifecycleScope.launch { val props = controller?.pickLabel(x, y)?.properties val questKey = props?.let { questPinsManager?.getQuestKey(it) } @@ -178,7 +178,7 @@ class QuestsMapFragment : LocationAwareMapFragment() { fun endFocusQuest() { clearFocusQuest() - lifecycleScope.launch { + viewLifecycleScope.launch { /* small delay to wait for other animations when ending focus on quest to be done first Most specifically, the map is being updated after a quest is solved, if the zoom out animation already starts while the map is being updated, there can be a little @@ -213,16 +213,16 @@ class QuestsMapFragment : LocationAwareMapFragment() { when (pinMode) { PinMode.QUESTS -> { - editHistoryPinsManager?.isActive = false - questPinsManager?.isActive = true + editHistoryPinsManager?.isVisible = false + questPinsManager?.isVisible = true } PinMode.EDITS -> { - questPinsManager?.isActive = false - editHistoryPinsManager?.isActive = true + questPinsManager?.isVisible = false + editHistoryPinsManager?.isVisible = true } else -> { - questPinsManager?.isActive = false - editHistoryPinsManager?.isActive = false + questPinsManager?.isVisible = false + editHistoryPinsManager?.isVisible = false } } } diff --git a/app/src/main/java/de/westnordost/streetcomplete/map/tangram/KtMapController.kt b/app/src/main/java/de/westnordost/streetcomplete/map/tangram/KtMapController.kt index f6af646211..d73a435806 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/map/tangram/KtMapController.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/map/tangram/KtMapController.kt @@ -51,7 +51,7 @@ class KtMapController(private val c: MapController, contentResolver: ContentReso private val pickLabelContinuations = ConcurrentLinkedQueue>() private val featurePickContinuations = ConcurrentLinkedQueue>() - private val lifecycleScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) + private val viewLifecycleScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) private var mapChangingListener: MapChangingListener? = null @@ -101,7 +101,7 @@ class KtMapController(private val c: MapController, contentResolver: ContentReso override fun onRegionWillChange(animated: Boolean) { // could be called not on the ui thread, see https://github.com/tangrams/tangram-es/issues/2157 - lifecycleScope.launch { + viewLifecycleScope.launch { calledOnMapIsChangingOnce = false if (!cameraManager.isAnimating) { mapChangingListener?.onMapWillChange() @@ -111,14 +111,14 @@ class KtMapController(private val c: MapController, contentResolver: ContentReso } override fun onRegionIsChanging() { - lifecycleScope.launch { + viewLifecycleScope.launch { if (!cameraManager.isAnimating) mapChangingListener?.onMapIsChanging() calledOnMapIsChangingOnce = true } } override fun onRegionDidChange(animated: Boolean) { - lifecycleScope.launch { + viewLifecycleScope.launch { if (!cameraManager.isAnimating) { if (!calledOnMapIsChangingOnce) mapChangingListener?.onMapIsChanging() mapChangingListener?.onMapDidChange() @@ -130,7 +130,7 @@ class KtMapController(private val c: MapController, contentResolver: ContentReso } @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) fun onDestroy() { - lifecycleScope.cancel() + viewLifecycleScope.cancel() cameraManager.cancelAllCameraAnimations() } diff --git a/app/src/main/java/de/westnordost/streetcomplete/notifications/OsmUnreadMessagesFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/notifications/OsmUnreadMessagesFragment.kt index 24ee2d2140..0390b0edf6 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/notifications/OsmUnreadMessagesFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/notifications/OsmUnreadMessagesFragment.kt @@ -10,13 +10,13 @@ import android.view.animation.DecelerateInterpolator import androidx.core.net.toUri import androidx.core.os.bundleOf import androidx.fragment.app.DialogFragment -import androidx.lifecycle.lifecycleScope import de.westnordost.streetcomplete.Injector import de.westnordost.streetcomplete.R import de.westnordost.streetcomplete.databinding.FragmentUnreadOsmMessageBinding import de.westnordost.streetcomplete.ktx.toPx import de.westnordost.streetcomplete.ktx.tryStartActivity import de.westnordost.streetcomplete.ktx.viewBinding +import de.westnordost.streetcomplete.ktx.viewLifecycleScope import de.westnordost.streetcomplete.util.SoundFx import kotlinx.coroutines.launch import javax.inject.Inject @@ -61,7 +61,7 @@ class OsmUnreadMessagesFragment : DialogFragment(R.layout.fragment_unread_osm_me private fun startAnimation() { val ctx = requireContext() - lifecycleScope.launch { soundFx.play(R.raw.sliding_envelope) } + viewLifecycleScope.launch { soundFx.play(R.raw.sliding_envelope) } binding.mailFrontImageView.alpha = 0f diff --git a/app/src/main/java/de/westnordost/streetcomplete/quests/AbstractBottomSheetFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/quests/AbstractBottomSheetFragment.kt index 5d4e6d1ecc..669f3b303a 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/quests/AbstractBottomSheetFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/quests/AbstractBottomSheetFragment.kt @@ -10,7 +10,6 @@ import androidx.annotation.UiThread import androidx.appcompat.app.AlertDialog import androidx.core.view.* import androidx.fragment.app.Fragment -import androidx.lifecycle.lifecycleScope import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_COLLAPSED import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED @@ -18,6 +17,7 @@ import de.westnordost.streetcomplete.R import de.westnordost.streetcomplete.data.osm.mapdata.LatLon import de.westnordost.streetcomplete.ktx.toPx import de.westnordost.streetcomplete.ktx.updateMargins +import de.westnordost.streetcomplete.ktx.viewLifecycleScope import de.westnordost.streetcomplete.view.RoundRectOutlineProvider import de.westnordost.streetcomplete.view.SlidingRelativeLayout import de.westnordost.streetcomplete.view.insets_animation.respectSystemInsets @@ -54,7 +54,7 @@ abstract class AbstractBottomSheetFragment : Fragment(), IsCloseableBottomSheet super.onViewCreated(view, savedInstanceState) bottomSheet.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ -> - lifecycleScope.launch { + viewLifecycleScope.launch { // not immediately because this is called during layout change (view.getTop() == 0) delay(1) updateCloseButtonVisibility() diff --git a/app/src/main/java/de/westnordost/streetcomplete/quests/SplitWayFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/quests/SplitWayFragment.kt index ec0b2cc1a3..239c84f5b3 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/quests/SplitWayFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/quests/SplitWayFragment.kt @@ -15,7 +15,6 @@ import androidx.core.os.bundleOf import androidx.core.view.isInvisible import androidx.core.view.updateLayoutParams import androidx.fragment.app.Fragment -import androidx.lifecycle.lifecycleScope import de.westnordost.streetcomplete.Injector import de.westnordost.streetcomplete.R import de.westnordost.streetcomplete.data.osm.edits.split_way.SplitAtLinePosition @@ -147,7 +146,7 @@ class SplitWayFragment : Fragment(R.layout.fragment_split_way), if (splits.isNotEmpty()) { val item = splits.removeAt(splits.lastIndex) animateButtonVisibilities() - lifecycleScope.launch { soundFx.play(R.raw.plop2) } + viewLifecycleScope.launch { soundFx.play(R.raw.plop2) } listener?.onRemoveSplit(item.second) } } @@ -195,7 +194,7 @@ class SplitWayFragment : Fragment(R.layout.fragment_split_way), animator.setTarget(binding.scissors) animator.start() - lifecycleScope.launch { soundFx.play(R.raw.snip) } + viewLifecycleScope.launch { soundFx.play(R.raw.snip) } } private fun createSplits(clickPosition: LatLon, clickAreaSizeInMeters: Double): Set { diff --git a/app/src/main/java/de/westnordost/streetcomplete/quests/lanes/AddLanesForm.kt b/app/src/main/java/de/westnordost/streetcomplete/quests/lanes/AddLanesForm.kt index 67f7572e74..eb0807e997 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/quests/lanes/AddLanesForm.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/quests/lanes/AddLanesForm.kt @@ -7,11 +7,11 @@ import android.view.View import android.view.ViewGroup import androidx.annotation.AnyThread import androidx.core.view.isGone -import androidx.lifecycle.lifecycleScope import de.westnordost.streetcomplete.R import de.westnordost.streetcomplete.data.osm.geometry.ElementPolylinesGeometry import de.westnordost.streetcomplete.databinding.QuestLanesSelectTypeBinding import de.westnordost.streetcomplete.databinding.QuestStreetLanesPuzzleBinding +import de.westnordost.streetcomplete.ktx.viewLifecycleScope import de.westnordost.streetcomplete.quests.AbstractQuestFormAnswerFragment import de.westnordost.streetcomplete.quests.AnswerItem import de.westnordost.streetcomplete.quests.StreetSideRotater @@ -150,7 +150,7 @@ class AddLanesForm : AbstractQuestFormAnswerFragment() { } private fun askLanesAndSwitchToStreetSideLayout() { - lifecycleScope.launch { + viewLifecycleScope.launch { val lanes = askForTotalNumberOfLanes() setTotalLanesCount(lanes) setStreetSideLayout() @@ -219,7 +219,7 @@ class AddLanesForm : AbstractQuestFormAnswerFragment() { //region Lane selection dialog private fun selectNumberOfLanesOnOneSide(isRight: Boolean) { - lifecycleScope.launch { + viewLifecycleScope.launch { setLanesCount(askForNumberOfLanesOnOneSide(isRight), isRight) } } @@ -236,7 +236,7 @@ class AddLanesForm : AbstractQuestFormAnswerFragment() { } private fun selectTotalNumberOfLanes() { - lifecycleScope.launch { + viewLifecycleScope.launch { setTotalLanesCount(askForTotalNumberOfLanes()) } } diff --git a/app/src/main/java/de/westnordost/streetcomplete/quests/oneway_suspects/AddSuspectedOnewayForm.kt b/app/src/main/java/de/westnordost/streetcomplete/quests/oneway_suspects/AddSuspectedOnewayForm.kt index e916916bfa..dfdf7d015c 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/quests/oneway_suspects/AddSuspectedOnewayForm.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/quests/oneway_suspects/AddSuspectedOnewayForm.kt @@ -3,14 +3,12 @@ package de.westnordost.streetcomplete.quests.oneway_suspects import android.os.Bundle import androidx.annotation.AnyThread import android.view.View -import androidx.lifecycle.lifecycleScope - import javax.inject.Inject - import de.westnordost.streetcomplete.Injector import de.westnordost.streetcomplete.R import de.westnordost.streetcomplete.data.osm.geometry.ElementPolylinesGeometry import de.westnordost.streetcomplete.databinding.QuestStreetSidePuzzleBinding +import de.westnordost.streetcomplete.ktx.viewLifecycleScope import de.westnordost.streetcomplete.quests.AbstractQuestAnswerFragment import de.westnordost.streetcomplete.quests.AnswerItem import de.westnordost.streetcomplete.quests.StreetSideRotater @@ -45,7 +43,7 @@ class AddSuspectedOnewayForm : AbstractQuestAnswerFragment lifecycleScope.launch { deleteCache() }} + .setPositiveButton(R.string.delete_confirmation) { _, _ -> viewLifecycleScope.launch { deleteCache() }} .setNegativeButton(android.R.string.cancel, null) .show() true } findPreference("quests.restore.hidden")?.setOnPreferenceClickListener { - lifecycleScope.launch { + viewLifecycleScope.launch { val hidden = questController.unhideAll() context?.toast(getString(R.string.restore_hidden_success, hidden), Toast.LENGTH_LONG) } diff --git a/app/src/main/java/de/westnordost/streetcomplete/settings/questselection/QuestPresetsAdapter.kt b/app/src/main/java/de/westnordost/streetcomplete/settings/questselection/QuestPresetsAdapter.kt index fc5c99e8bf..308f570542 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/settings/questselection/QuestPresetsAdapter.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/settings/questselection/QuestPresetsAdapter.kt @@ -24,19 +24,19 @@ class QuestPresetsAdapter @Inject constructor( private var presets: MutableList = mutableListOf() - private val lifecycleScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) + private val viewLifecycleScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) private val questPresetsListener = object : QuestPresetsSource.Listener { - override fun onSelectedQuestPresetChanged() { lifecycleScope.launch { + override fun onSelectedQuestPresetChanged() { viewLifecycleScope.launch { notifyDataSetChanged() }} - override fun onAddedQuestPreset(preset: QuestPreset) { lifecycleScope.launch { + override fun onAddedQuestPreset(preset: QuestPreset) { viewLifecycleScope.launch { presets.add(preset) notifyItemInserted(presets.size - 1) }} - override fun onDeletedQuestPreset(presetId: Long) { lifecycleScope.launch { + override fun onDeletedQuestPreset(presetId: Long) { viewLifecycleScope.launch { val deleteIndex = presets.indexOfFirst { it.id == presetId } presets.removeAt(deleteIndex) notifyItemRemoved(deleteIndex) @@ -59,7 +59,7 @@ class QuestPresetsAdapter @Inject constructor( @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) fun onDestroy() { - lifecycleScope.cancel() + viewLifecycleScope.cancel() } override fun getItemCount(): Int = presets.size @@ -89,7 +89,7 @@ class QuestPresetsAdapter @Inject constructor( } fun onSelectQuestPreset(presetId: Long) { - lifecycleScope.launch(Dispatchers.IO) { + viewLifecycleScope.launch(Dispatchers.IO) { questPresetsController.selectedQuestPresetId = presetId } } @@ -104,7 +104,7 @@ class QuestPresetsAdapter @Inject constructor( fun deleteQuestPreset(presetId: Long) { binding.deleteButton.isEnabled = false - lifecycleScope.launch(Dispatchers.IO) { + viewLifecycleScope.launch(Dispatchers.IO) { questPresetsController.deleteQuestPreset(presetId) } } diff --git a/app/src/main/java/de/westnordost/streetcomplete/settings/questselection/QuestPresetsFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/settings/questselection/QuestPresetsFragment.kt index c6c5edd68c..a85720ce4c 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/settings/questselection/QuestPresetsFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/settings/questselection/QuestPresetsFragment.kt @@ -4,7 +4,6 @@ import android.os.Bundle import android.view.View import androidx.appcompat.app.AlertDialog import androidx.fragment.app.Fragment -import androidx.lifecycle.lifecycleScope import de.westnordost.streetcomplete.HasTitle import de.westnordost.streetcomplete.Injector import de.westnordost.streetcomplete.R @@ -12,6 +11,7 @@ import de.westnordost.streetcomplete.data.visiblequests.QuestPresetsController import de.westnordost.streetcomplete.databinding.DialogInputTextBinding import de.westnordost.streetcomplete.databinding.FragmentQuestPresetsBinding import de.westnordost.streetcomplete.ktx.viewBinding +import de.westnordost.streetcomplete.ktx.viewLifecycleScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import javax.inject.Inject @@ -34,10 +34,10 @@ class QuestPresetsFragment : Fragment(R.layout.fragment_quest_presets), HasTitle override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.questProfilesList.adapter = questPresetsAdapter - binding.addPresetButton.setOnClickListener { onClickAddProfile() } + binding.addPresetButton.setOnClickListener { onClickAddPreset() } } - private fun onClickAddProfile() { + private fun onClickAddPreset() { val ctx = context ?: return val dialogBinding = DialogInputTextBinding.inflate(layoutInflater) @@ -48,8 +48,8 @@ class QuestPresetsFragment : Fragment(R.layout.fragment_quest_presets), HasTitle .setView(dialogBinding.root) .setPositiveButton(android.R.string.ok) { _,_ -> val name = dialogBinding.editText.text.toString().trim() - lifecycleScope.launch(Dispatchers.IO) { - questPresetsController.addQuestProfile(name) + viewLifecycleScope.launch(Dispatchers.IO) { + questPresetsController.addQuestPreset(name) } } .setNegativeButton(android.R.string.cancel, null) diff --git a/app/src/main/java/de/westnordost/streetcomplete/settings/questselection/QuestSelectionAdapter.kt b/app/src/main/java/de/westnordost/streetcomplete/settings/questselection/QuestSelectionAdapter.kt index 2e9151eafa..ec1c50a3bc 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/settings/questselection/QuestSelectionAdapter.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/settings/questselection/QuestSelectionAdapter.kt @@ -8,10 +8,7 @@ import androidx.recyclerview.widget.ItemTouchHelper import android.view.LayoutInflater import android.view.MotionEvent import android.view.ViewGroup -import android.widget.CheckBox import android.widget.CompoundButton -import android.widget.ImageView -import android.widget.TextView import androidx.core.content.ContextCompat import androidx.core.view.isGone import androidx.core.view.isInvisible @@ -53,7 +50,7 @@ class QuestSelectionAdapter @Inject constructor( private val currentCountryCodes: List private val itemTouchHelper by lazy { ItemTouchHelper(TouchHelperCallback()) } - private val lifecycleScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) + private val viewLifecycleScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) /** all quest types */ private var questTypes: MutableList = mutableListOf() @@ -101,7 +98,7 @@ class QuestSelectionAdapter @Inject constructor( override fun onQuestTypeVisibilitiesChanged() { // all/many visibilities have changed - update the data and notify UI of changes - lifecycleScope.launch { + viewLifecycleScope.launch { withContext(Dispatchers.IO) { questTypes.forEach { it.visible = visibleQuestTypeController.isVisible(it.questType) } } @@ -123,7 +120,7 @@ class QuestSelectionAdapter @Inject constructor( override fun onQuestTypeOrdersChanged() { // all/many quest orders have been changed - reinit list - lifecycleScope.launch { questTypes = createQuestTypeVisibilityList() } + viewLifecycleScope.launch { questTypes = createQuestTypeVisibilityList() } } } @@ -135,7 +132,7 @@ class QuestSelectionAdapter @Inject constructor( @OnLifecycleEvent(Lifecycle.Event.ON_START) fun onStart() { - lifecycleScope.launch { questTypes = createQuestTypeVisibilityList() } + viewLifecycleScope.launch { questTypes = createQuestTypeVisibilityList() } visibleQuestTypeController.addListener(visibleQuestsListener) questTypeOrderController.addListener(questTypeOrderListener) @@ -149,7 +146,7 @@ class QuestSelectionAdapter @Inject constructor( @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) fun onDestroy() { - lifecycleScope.cancel() + viewLifecycleScope.cancel() } override fun onAttachedToRecyclerView(recyclerView: RecyclerView) { @@ -222,7 +219,7 @@ class QuestSelectionAdapter @Inject constructor( val item = qt[draggedTo].questType val toAfter = qt[draggedTo - 1].questType - lifecycleScope.launch(Dispatchers.IO) { + viewLifecycleScope.launch(Dispatchers.IO) { questTypeOrderController.addOrderItem(item, toAfter) } } @@ -305,7 +302,7 @@ class QuestSelectionAdapter @Inject constructor( override fun onCheckedChanged(compoundButton: CompoundButton, b: Boolean) { item.visible = b updateSelectionStatus() - lifecycleScope.launch(Dispatchers.IO) { + viewLifecycleScope.launch(Dispatchers.IO) { visibleQuestTypeController.setVisible(item.questType, item.visible) } if (b && item.questType.defaultDisabledMessage > 0) { diff --git a/app/src/main/java/de/westnordost/streetcomplete/settings/questselection/QuestSelectionFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/settings/questselection/QuestSelectionFragment.kt index 98dd862bec..14a824bc91 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/settings/questselection/QuestSelectionFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/settings/questselection/QuestSelectionFragment.kt @@ -9,7 +9,6 @@ import androidx.appcompat.app.AlertDialog import androidx.appcompat.widget.SearchView import androidx.core.view.isInvisible import androidx.fragment.app.Fragment -import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager import de.westnordost.streetcomplete.DisplaysTitle @@ -24,6 +23,7 @@ import de.westnordost.streetcomplete.data.visiblequests.VisibleQuestTypeControll import de.westnordost.streetcomplete.data.visiblequests.VisibleQuestTypeSource import de.westnordost.streetcomplete.databinding.FragmentQuestSelectionBinding import de.westnordost.streetcomplete.ktx.viewBinding +import de.westnordost.streetcomplete.ktx.viewLifecycleScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import javax.inject.Inject @@ -56,13 +56,13 @@ class QuestSelectionFragment : Fragment(R.layout.fragment_quest_selection), HasT } private val visibleQuestTypeListener = object : VisibleQuestTypeSource.Listener { - override fun onQuestTypeVisibilityChanged(questType: QuestType<*>, visible: Boolean) { lifecycleScope.launch { updateTitle() } } - override fun onQuestTypeVisibilitiesChanged() { lifecycleScope.launch { updateTitle() } } + override fun onQuestTypeVisibilityChanged(questType: QuestType<*>, visible: Boolean) { viewLifecycleScope.launch { updateTitle() } } + override fun onQuestTypeVisibilitiesChanged() { viewLifecycleScope.launch { updateTitle() } } } init { Injector.applicationComponent.inject(this) - lifecycle.addObserver(questSelectionAdapter) + viewLifecycleOwner.lifecycle.addObserver(questSelectionAdapter) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -124,14 +124,14 @@ class QuestSelectionFragment : Fragment(R.layout.fragment_quest_selection), HasT } private fun resetQuestVisibilitiesAndOrder() { - lifecycleScope.launch(Dispatchers.IO) { + viewLifecycleScope.launch(Dispatchers.IO) { visibleQuestTypeController.clear() questTypeOrderController.clear() } } private fun deselectAllQuests() { - lifecycleScope.launch(Dispatchers.IO) { + viewLifecycleScope.launch(Dispatchers.IO) { visibleQuestTypeController.setAllVisible(questTypeRegistry, false) } } diff --git a/app/src/main/java/de/westnordost/streetcomplete/tutorial/TutorialFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/tutorial/TutorialFragment.kt index 071035374c..a3092c2762 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/tutorial/TutorialFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/tutorial/TutorialFragment.kt @@ -9,12 +9,12 @@ import android.view.animation.AccelerateDecelerateInterpolator import android.view.animation.AccelerateInterpolator import android.view.animation.BounceInterpolator import androidx.fragment.app.Fragment -import androidx.lifecycle.lifecycleScope import de.westnordost.streetcomplete.R import de.westnordost.streetcomplete.databinding.FragmentTutorialBinding import de.westnordost.streetcomplete.ktx.toDp import de.westnordost.streetcomplete.ktx.toPx import de.westnordost.streetcomplete.ktx.viewBinding +import de.westnordost.streetcomplete.ktx.viewLifecycleScope import de.westnordost.streetcomplete.location.LocationState import de.westnordost.streetcomplete.view.insets_animation.respectSystemInsets import kotlinx.coroutines.delay @@ -68,7 +68,7 @@ class TutorialFragment : Fragment(R.layout.fragment_tutorial) { activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED } - private fun step1Transition() = lifecycleScope.launch { + private fun step1Transition() = viewLifecycleScope.launch { val ctx = requireContext() updateIndicatorDots() @@ -155,7 +155,7 @@ class TutorialFragment : Fragment(R.layout.fragment_tutorial) { } } - private fun step2Transition() = lifecycleScope.launch { + private fun step2Transition() = viewLifecycleScope.launch { val ctx = requireContext() updateIndicatorDots() diff --git a/app/src/main/java/de/westnordost/streetcomplete/user/AchievementsFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/user/AchievementsFragment.kt index 7ef19a4860..d6b81cea53 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/user/AchievementsFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/user/AchievementsFragment.kt @@ -7,7 +7,6 @@ import android.view.ViewGroup import androidx.core.view.isGone import androidx.core.view.updateLayoutParams import androidx.fragment.app.Fragment -import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView import de.westnordost.streetcomplete.Injector @@ -20,6 +19,7 @@ import de.westnordost.streetcomplete.databinding.FragmentAchievementsBinding import de.westnordost.streetcomplete.ktx.awaitLayout import de.westnordost.streetcomplete.ktx.toPx import de.westnordost.streetcomplete.ktx.viewBinding +import de.westnordost.streetcomplete.ktx.viewLifecycleScope import de.westnordost.streetcomplete.view.GridLayoutSpacingItemDecoration import de.westnordost.streetcomplete.view.ListAdapter import kotlinx.coroutines.* @@ -53,7 +53,7 @@ class AchievementsFragment : Fragment(R.layout.fragment_achievements) { val minCellWidth = 144f.toPx(ctx) val itemSpacing = ctx.resources.getDimensionPixelSize(R.dimen.achievements_item_margin) - viewLifecycleOwner.lifecycleScope.launch { + viewLifecycleScope.launch { view.awaitLayout() binding.emptyText.visibility = View.GONE diff --git a/app/src/main/java/de/westnordost/streetcomplete/user/BallPitView.kt b/app/src/main/java/de/westnordost/streetcomplete/user/BallPitView.kt index 2529d61919..27b39bf687 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/user/BallPitView.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/user/BallPitView.kt @@ -50,7 +50,7 @@ class BallPitView @JvmOverloads constructor( private val mainHandler = Handler(Looper.getMainLooper()) - private val lifecycleScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) + private val viewLifecycleScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) private val sensorEventListener = object : SensorEventListener { override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) { } @@ -99,7 +99,7 @@ class BallPitView @JvmOverloads constructor( @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) fun onDestroy() { physicsController.destroy() - lifecycleScope.cancel() + viewLifecycleScope.cancel() } fun setViews(viewsAndSizes: List>) { @@ -109,7 +109,7 @@ class BallPitView @JvmOverloads constructor( } val areaInMeters = max(1f, viewsAndSizes.map { it.second }.sumByFloat { getBubbleArea(it) }) - lifecycleScope.launch { + viewLifecycleScope.launch { setupScene(areaInMeters / BALLPIT_FILL_FACTOR) addBubblesToScene(viewsAndSizes) } diff --git a/app/src/main/java/de/westnordost/streetcomplete/user/LinksFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/user/LinksFragment.kt index c80eb14d62..599d353a73 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/user/LinksFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/user/LinksFragment.kt @@ -6,7 +6,6 @@ import android.view.View import androidx.core.net.toUri import androidx.core.view.isGone import androidx.fragment.app.Fragment -import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView import de.westnordost.streetcomplete.Injector @@ -14,10 +13,7 @@ import de.westnordost.streetcomplete.R import de.westnordost.streetcomplete.data.user.UserStore import de.westnordost.streetcomplete.data.user.achievements.UserLinksSource import de.westnordost.streetcomplete.databinding.FragmentLinksBinding -import de.westnordost.streetcomplete.ktx.awaitLayout -import de.westnordost.streetcomplete.ktx.toDp -import de.westnordost.streetcomplete.ktx.tryStartActivity -import de.westnordost.streetcomplete.ktx.viewBinding +import de.westnordost.streetcomplete.ktx.* import de.westnordost.streetcomplete.view.GridLayoutSpacingItemDecoration import kotlinx.coroutines.* import javax.inject.Inject @@ -40,7 +36,7 @@ class LinksFragment : Fragment(R.layout.fragment_links) { val minCellWidth = 280f val itemSpacing = ctx.resources.getDimensionPixelSize(R.dimen.links_item_margin) - viewLifecycleOwner.lifecycleScope.launch { + viewLifecycleScope.launch { view.awaitLayout() binding.emptyText.visibility = View.GONE diff --git a/app/src/main/java/de/westnordost/streetcomplete/user/LoginFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/user/LoginFragment.kt index 4eb77c3e5a..9e4d03fb5f 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/user/LoginFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/user/LoginFragment.kt @@ -9,7 +9,6 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager.POP_BACK_STACK_INCLUSIVE import androidx.fragment.app.commit import androidx.fragment.app.replace -import androidx.lifecycle.lifecycleScope import de.westnordost.osmapi.user.Permission import de.westnordost.osmapi.user.PermissionsApi import de.westnordost.streetcomplete.BackPressedListener @@ -23,6 +22,7 @@ import de.westnordost.streetcomplete.databinding.FragmentLoginBinding import de.westnordost.streetcomplete.ktx.childFragmentManagerOrNull import de.westnordost.streetcomplete.ktx.toast import de.westnordost.streetcomplete.ktx.viewBinding +import de.westnordost.streetcomplete.ktx.viewLifecycleScope import de.westnordost.streetcomplete.settings.OAuthFragment import kotlinx.coroutines.* import oauth.signpost.OAuthConsumer @@ -62,7 +62,7 @@ class LoginFragment : Fragment(R.layout.fragment_login), override fun onStart() { super.onStart() - lifecycleScope.launch { + viewLifecycleScope.launch { val unsyncedChanges = unsyncedChangesCountSource.getCount() binding.unpublishedQuestsText.text = getString(R.string.unsynced_quests_not_logged_in_description, unsyncedChanges) binding.unpublishedQuestsText.isGone = unsyncedChanges <= 0 @@ -85,7 +85,7 @@ class LoginFragment : Fragment(R.layout.fragment_login), binding.loginButton.visibility = View.INVISIBLE binding.loginProgress.visibility = View.VISIBLE childFragmentManager.popBackStack("oauth", POP_BACK_STACK_INCLUSIVE) - lifecycleScope.launch { + viewLifecycleScope.launch { if (hasRequiredPermissions(consumer)) { userController.logIn(consumer) } else { diff --git a/app/src/main/java/de/westnordost/streetcomplete/user/ProfileFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/user/ProfileFragment.kt index 7554b0a1f2..f7169368ac 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/user/ProfileFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/user/ProfileFragment.kt @@ -9,7 +9,6 @@ import android.view.View import androidx.core.net.toUri import androidx.core.view.isGone import androidx.fragment.app.Fragment -import androidx.lifecycle.lifecycleScope import de.westnordost.streetcomplete.Injector import de.westnordost.streetcomplete.R import de.westnordost.streetcomplete.data.osmnotes.NotesModule @@ -20,6 +19,7 @@ import de.westnordost.streetcomplete.databinding.FragmentProfileBinding import de.westnordost.streetcomplete.ktx.createBitmap import de.westnordost.streetcomplete.ktx.tryStartActivity import de.westnordost.streetcomplete.ktx.viewBinding +import de.westnordost.streetcomplete.ktx.viewLifecycleScope import kotlinx.coroutines.* import java.io.File import java.util.Locale @@ -40,19 +40,19 @@ class ProfileFragment : Fragment(R.layout.fragment_profile) { private val binding by viewBinding(FragmentProfileBinding::bind) private val unsyncedChangesCountListener = object : UnsyncedChangesCountSource.Listener { - override fun onIncreased() { lifecycleScope.launch { updateUnpublishedQuestsText() } } - override fun onDecreased() { lifecycleScope.launch { updateUnpublishedQuestsText() } } + override fun onIncreased() { viewLifecycleScope.launch { updateUnpublishedQuestsText() } } + override fun onDecreased() { viewLifecycleScope.launch { updateUnpublishedQuestsText() } } } private val questStatisticsDaoListener = object : QuestStatisticsDao.Listener { - override fun onAddedOne(questType: String) { lifecycleScope.launch { updateSolvedQuestsText() }} - override fun onSubtractedOne(questType: String) { lifecycleScope.launch { updateSolvedQuestsText() } } - override fun onReplacedAll() { lifecycleScope.launch { updateSolvedQuestsText() } } + override fun onAddedOne(questType: String) { viewLifecycleScope.launch { updateSolvedQuestsText() }} + override fun onSubtractedOne(questType: String) { viewLifecycleScope.launch { updateSolvedQuestsText() } } + override fun onReplacedAll() { viewLifecycleScope.launch { updateSolvedQuestsText() } } } private val userStoreUpdateListener = object : UserStore.UpdateListener { - override fun onUserDataUpdated() { lifecycleScope.launch { updateUserName() } } + override fun onUserDataUpdated() { viewLifecycleScope.launch { updateUserName() } } } private val userAvatarListener = object : UserAvatarListener { - override fun onUserAvatarUpdated() { lifecycleScope.launch { updateAvatar() } } + override fun onUserAvatarUpdated() { viewLifecycleScope.launch { updateAvatar() } } } init { @@ -78,7 +78,7 @@ class ProfileFragment : Fragment(R.layout.fragment_profile) { override fun onStart() { super.onStart() - lifecycleScope.launch { + viewLifecycleScope.launch { userStore.addListener(userStoreUpdateListener) userController.addUserAvatarListener(userAvatarListener) questStatisticsDao.addListener(questStatisticsDaoListener) diff --git a/app/src/main/java/de/westnordost/streetcomplete/user/QuestStatisticsByCountryFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/user/QuestStatisticsByCountryFragment.kt index 649bbb217c..86511d2d06 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/user/QuestStatisticsByCountryFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/user/QuestStatisticsByCountryFragment.kt @@ -4,13 +4,13 @@ import android.os.Bundle import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment -import androidx.lifecycle.lifecycleScope import de.westnordost.streetcomplete.Injector import de.westnordost.streetcomplete.R import de.westnordost.streetcomplete.data.user.CountryStatisticsDao import de.westnordost.streetcomplete.databinding.FragmentQuestStatisticsBallPitBinding import de.westnordost.streetcomplete.ktx.toPx import de.westnordost.streetcomplete.ktx.viewBinding +import de.westnordost.streetcomplete.ktx.viewLifecycleScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -37,7 +37,7 @@ class QuestStatisticsByCountryFragment : Fragment(R.layout.fragment_quest_statis lifecycle.addObserver(binding.ballPitView) - lifecycleScope.launch { + viewLifecycleScope.launch { val countriesStatistics = withContext(Dispatchers.IO) { countryStatisticsDao.getAll() } binding.ballPitView.setViews(countriesStatistics.map { diff --git a/app/src/main/java/de/westnordost/streetcomplete/user/QuestStatisticsByQuestTypeFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/user/QuestStatisticsByQuestTypeFragment.kt index 4ba349c287..0a425426a6 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/user/QuestStatisticsByQuestTypeFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/user/QuestStatisticsByQuestTypeFragment.kt @@ -8,7 +8,6 @@ import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.widget.FrameLayout import android.widget.ImageView import androidx.fragment.app.Fragment -import androidx.lifecycle.lifecycleScope import de.westnordost.streetcomplete.Injector import de.westnordost.streetcomplete.R import de.westnordost.streetcomplete.data.quest.QuestType @@ -17,6 +16,7 @@ import de.westnordost.streetcomplete.data.user.QuestStatisticsDao import de.westnordost.streetcomplete.databinding.FragmentQuestStatisticsBallPitBinding import de.westnordost.streetcomplete.ktx.toPx import de.westnordost.streetcomplete.ktx.viewBinding +import de.westnordost.streetcomplete.ktx.viewLifecycleScope import de.westnordost.streetcomplete.view.CircularOutlineProvider import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -48,7 +48,7 @@ class QuestStatisticsByQuestTypeFragment : Fragment(R.layout.fragment_quest_stat lifecycle.addObserver(binding.ballPitView) - lifecycleScope.launch { + viewLifecycleScope.launch { val solvedQuestsByQuestType = getSolvedQuestsByQuestType() binding.ballPitView.setViews(solvedQuestsByQuestType.map { (questType, amount) -> createQuestTypeBubbleView(questType, amount) to amount diff --git a/app/src/main/java/de/westnordost/streetcomplete/user/QuestStatisticsFragment.kt b/app/src/main/java/de/westnordost/streetcomplete/user/QuestStatisticsFragment.kt index e1b3c8c4cd..0456612860 100644 --- a/app/src/main/java/de/westnordost/streetcomplete/user/QuestStatisticsFragment.kt +++ b/app/src/main/java/de/westnordost/streetcomplete/user/QuestStatisticsFragment.kt @@ -6,7 +6,6 @@ import androidx.core.view.isGone import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentTransaction.TRANSIT_FRAGMENT_FADE import androidx.fragment.app.commit -import androidx.lifecycle.lifecycleScope import de.westnordost.streetcomplete.Injector import de.westnordost.streetcomplete.R import de.westnordost.streetcomplete.data.quest.QuestType @@ -14,6 +13,7 @@ import de.westnordost.streetcomplete.data.user.QuestStatisticsDao import de.westnordost.streetcomplete.data.user.UserStore import de.westnordost.streetcomplete.databinding.FragmentQuestStatisticsBinding import de.westnordost.streetcomplete.ktx.viewBinding +import de.westnordost.streetcomplete.ktx.viewLifecycleScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -42,7 +42,7 @@ class QuestStatisticsFragment : Fragment(R.layout.fragment_quest_statistics), override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - lifecycleScope.launch { + viewLifecycleScope.launch { binding.emptyText.isGone = withContext(Dispatchers.IO) { questStatisticsDao.getTotalAmount() != 0 } } diff --git a/app/src/test/java/de/westnordost/streetcomplete/data/visiblequests/QuestPresetControllerTest.kt b/app/src/test/java/de/westnordost/streetcomplete/data/visiblequests/QuestPresetControllerTest.kt index bd66c7136f..5de79caef0 100644 --- a/app/src/test/java/de/westnordost/streetcomplete/data/visiblequests/QuestPresetControllerTest.kt +++ b/app/src/test/java/de/westnordost/streetcomplete/data/visiblequests/QuestPresetControllerTest.kt @@ -39,7 +39,7 @@ class QuestPresetControllerTest { @Test fun add() { on(questPresetsDao.add(any())).thenReturn(123) - ctrl.addQuestProfile("test") + ctrl.addQuestPreset("test") verify(questPresetsDao).add("test") verify(listener).onAddedQuestPreset(QuestPreset(123, "test")) }