From b682075d1614bde20d32ed47098e9af15d56c0ca Mon Sep 17 00:00:00 2001 From: Aitor Viana Date: Wed, 29 Jan 2025 17:51:39 +0000 Subject: [PATCH] Ensure custom views cancel coroutine scopes in onDetachedFromWindow (#5551) Task/Issue URL: https://app.asana.com/0/1198194956794324/1209242164061666/f ### Description Ensure all custom views cancel coroutineScope ### Steps to test this PR Smoke tests features that related to the custom view changes --- .../AppTrackingProtectionNewTabSettingView.kt | 25 +++++++++------- .../newtab/AppTrackingProtectionStateView.kt | 24 ++++++++------- .../setting/FaviconFetchingSyncSetting.kt | 15 +++------- .../IndonesiaNewTabSectionView.kt | 25 +++++++++------- .../browser/newtab/NewTabLegacyPageView.kt | 28 ++++++++--------- .../app/browser/omnibar/OmnibarLayout.kt | 25 +++++++++------- .../survey/SurveyInPasswordsPromotion.kt | 9 ++---- .../sync/CredentialsSyncPausedView.kt | 18 +++++------ .../common/ui/notifyme/NotifyMeView.kt | 24 ++++++++------- .../settings/LegacyProSettingNetPView.kt | 24 ++++++++------- .../settings/ProSettingNetPView.kt | 23 +++++++------- .../shortcuts/ShortcutsNewTabSectionView.kt | 26 +++++++++------- .../shortcuts/ShortcutsNewTabSettingView.kt | 27 ++++++++++------- .../newtabpage/impl/view/NewTabPageView.kt | 25 +++++++++------- .../impl/newtab/RemoteMessageView.kt | 29 +++++++++++------- .../newtab/FavouritesNewTabSectionView.kt | 27 +++++++++++------ .../newtab/FavouritesNewTabSettingView.kt | 27 ++++++++++------- .../impl/sync/DisplayModeSyncSetting.kt | 15 +++------- .../impl/sync/SavedSiteInvalidItemsView.kt | 18 +++++------ .../impl/sync/SavedSiteSyncPausedView.kt | 18 +++++------ .../impl/settings/views/ItrSettingView.kt | 19 +++++------- .../settings/views/LegacyItrSettingView.kt | 18 +++++------ .../settings/views/LegacyPirSettingView.kt | 18 +++++------ .../settings/views/LegacyProSettingView.kt | 17 +++++------ .../impl/settings/views/PirSettingView.kt | 22 ++++++-------- .../impl/settings/views/ProSettingView.kt | 17 +++++------ .../bookmarks/SyncBookmarksPromotion.kt | 9 ++---- .../passwords/SyncPasswordsPromotion.kt | 9 ++---- .../sync/impl/ui/SyncDisabledView.kt | 22 +++++++------- .../duckduckgo/sync/impl/ui/SyncErrorView.kt | 22 +++++++------- .../sync/impl/ui/qrcode/SyncBarcodeView.kt | 30 ++++++++++++------- 31 files changed, 340 insertions(+), 315 deletions(-) diff --git a/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/newtab/AppTrackingProtectionNewTabSettingView.kt b/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/newtab/AppTrackingProtectionNewTabSettingView.kt index e65334ac144d..fd83de275575 100644 --- a/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/newtab/AppTrackingProtectionNewTabSettingView.kt +++ b/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/newtab/AppTrackingProtectionNewTabSettingView.kt @@ -16,7 +16,6 @@ package com.duckduckgo.mobile.android.vpn.ui.newtab -import android.annotation.SuppressLint import android.content.Context import android.util.AttributeSet import android.view.View @@ -24,10 +23,13 @@ import android.widget.LinearLayout import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.findViewTreeViewModelStoreOwner +import androidx.lifecycle.lifecycleScope import com.duckduckgo.anvil.annotations.ContributesRemoteFeature import com.duckduckgo.anvil.annotations.InjectWith import com.duckduckgo.anvil.annotations.PriorityKey import com.duckduckgo.common.ui.viewbinding.viewBinding +import com.duckduckgo.common.utils.ConflatedJob +import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.common.utils.ViewViewModelFactory import com.duckduckgo.di.scopes.ActivityScope import com.duckduckgo.di.scopes.AppScope @@ -42,9 +44,7 @@ import com.duckduckgo.newtabpage.api.NewTabPageSectionSettingsPlugin import com.squareup.anvil.annotations.ContributesMultibinding import dagger.android.support.AndroidSupportInjection import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -58,9 +58,12 @@ class AppTrackingProtectionNewTabSettingView @JvmOverloads constructor( @Inject lateinit var viewModelFactory: ViewViewModelFactory + @Inject + lateinit var dispatchers: DispatcherProvider + private val binding: ViewApptpSettingsItemBinding by viewBinding() - private var coroutineScope: CoroutineScope? = null + private val conflatedJob = ConflatedJob() private val viewModel: AppTrackingProtectionNewTabSettingsViewModel by lazy { ViewModelProvider(findViewTreeViewModelStoreOwner()!!, viewModelFactory)[AppTrackingProtectionNewTabSettingsViewModel::class.java] @@ -71,12 +74,14 @@ class AppTrackingProtectionNewTabSettingView @JvmOverloads constructor( super.onAttachedToWindow() findViewTreeLifecycleOwner()?.lifecycle?.addObserver(viewModel) - @SuppressLint("NoHardcodedCoroutineDispatcher") - coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) - - viewModel.viewState + conflatedJob += viewModel.viewState .onEach { render(it) } - .launchIn(coroutineScope!!) + .launchIn(findViewTreeLifecycleOwner()?.lifecycleScope!!) + } + + override fun onDetachedFromWindow() { + conflatedJob.cancel() + super.onDetachedFromWindow() } private fun render(viewState: ViewState) { diff --git a/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/newtab/AppTrackingProtectionStateView.kt b/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/newtab/AppTrackingProtectionStateView.kt index df3f60cf4a12..015bcc7fb22b 100644 --- a/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/newtab/AppTrackingProtectionStateView.kt +++ b/app-tracking-protection/vpn-impl/src/main/java/com/duckduckgo/mobile/android/vpn/ui/newtab/AppTrackingProtectionStateView.kt @@ -16,7 +16,6 @@ package com.duckduckgo.mobile.android.vpn.ui.newtab -import android.annotation.SuppressLint import android.content.Context import android.util.AttributeSet import android.view.View @@ -28,6 +27,8 @@ import com.duckduckgo.anvil.annotations.InjectWith import com.duckduckgo.common.ui.view.gone import com.duckduckgo.common.ui.view.show import com.duckduckgo.common.ui.viewbinding.viewBinding +import com.duckduckgo.common.utils.ConflatedJob +import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.common.utils.ViewViewModelFactory import com.duckduckgo.di.scopes.AppScope import com.duckduckgo.di.scopes.ViewScope @@ -46,9 +47,7 @@ import com.duckduckgo.newtabpage.api.NewTabPageSection import com.duckduckgo.newtabpage.api.NewTabPageSectionPlugin import dagger.android.support.AndroidSupportInjection import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -69,11 +68,14 @@ class AppTrackingProtectionStateView @JvmOverloads constructor( @Inject lateinit var viewModelFactory: ViewViewModelFactory + @Inject + lateinit var dispatchers: DispatcherProvider + private val viewModel: PrivacyReportViewModel by lazy { ViewModelProvider(findViewTreeViewModelStoreOwner()!!, viewModelFactory)[PrivacyReportViewModel::class.java] } - private var coroutineScope: CoroutineScope? = null + private val conflatedJob = ConflatedJob() private val binding: FragmentDeviceShieldCtaBinding by viewBinding() @@ -81,18 +83,20 @@ class AppTrackingProtectionStateView @JvmOverloads constructor( AndroidSupportInjection.inject(this) super.onAttachedToWindow() - @SuppressLint("NoHardcodedCoroutineDispatcher") - coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) - - viewModel.viewStateFlow + conflatedJob += viewModel.viewStateFlow .onEach { viewState -> renderViewState(viewState) } - .launchIn(coroutineScope!!) + .launchIn(findViewTreeLifecycleOwner()?.lifecycleScope!!) deviceShieldPixels.didShowNewTabSummary() configureViewReferences() } + override fun onDetachedFromWindow() { + conflatedJob.cancel() + super.onDetachedFromWindow() + } + private fun configureViewReferences() { binding.deviceShieldCtaLayout.setOnClickListener { deviceShieldPixels.didPressNewTabSummary() diff --git a/app/src/main/java/com/duckduckgo/app/browser/favicon/setting/FaviconFetchingSyncSetting.kt b/app/src/main/java/com/duckduckgo/app/browser/favicon/setting/FaviconFetchingSyncSetting.kt index d674e19ea493..1cc18a160222 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/favicon/setting/FaviconFetchingSyncSetting.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/favicon/setting/FaviconFetchingSyncSetting.kt @@ -16,7 +16,6 @@ package com.duckduckgo.app.browser.favicon.setting -import android.annotation.SuppressLint import android.content.* import android.util.* import android.widget.* @@ -26,13 +25,11 @@ import com.duckduckgo.app.browser.databinding.ViewSyncFaviconsFetchingBinding import com.duckduckgo.app.browser.favicon.setting.FaviconFetchingViewModel.ViewState import com.duckduckgo.common.ui.viewbinding.viewBinding import com.duckduckgo.common.utils.ConflatedJob +import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.di.scopes.* import com.duckduckgo.saved.sites.impl.databinding.* import dagger.android.support.* import javax.inject.* -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -47,7 +44,8 @@ class FaviconFetchingSyncSetting @JvmOverloads constructor( @Inject lateinit var viewModelFactory: FaviconFetchingViewModel.Factory - private var coroutineScope: CoroutineScope? = null + @Inject + lateinit var dispatchers: DispatcherProvider private var job: ConflatedJob = ConflatedJob() @@ -65,19 +63,14 @@ class FaviconFetchingSyncSetting @JvmOverloads constructor( viewModel.onFaviconFetchingSettingChanged(isChecked) } - @SuppressLint("NoHardcodedCoroutineDispatcher") - coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) - job += viewModel.viewState() .onEach { render(it) } - .launchIn(coroutineScope!!) + .launchIn(findViewTreeLifecycleOwner()?.lifecycleScope!!) } override fun onDetachedFromWindow() { super.onDetachedFromWindow() - coroutineScope?.cancel() job.cancel() - coroutineScope = null } private fun render(it: ViewState) { diff --git a/app/src/main/java/com/duckduckgo/app/browser/indonesiamessage/IndonesiaNewTabSectionView.kt b/app/src/main/java/com/duckduckgo/app/browser/indonesiamessage/IndonesiaNewTabSectionView.kt index 70aa396f43bc..cf157bd2b262 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/indonesiamessage/IndonesiaNewTabSectionView.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/indonesiamessage/IndonesiaNewTabSectionView.kt @@ -16,7 +16,6 @@ package com.duckduckgo.app.browser.indonesiamessage -import android.annotation.SuppressLint import android.content.Context import android.util.AttributeSet import android.view.View @@ -24,6 +23,7 @@ import android.widget.FrameLayout import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.findViewTreeViewModelStoreOwner +import androidx.lifecycle.lifecycleScope import com.duckduckgo.anvil.annotations.ContributesActivePlugin import com.duckduckgo.anvil.annotations.InjectWith import com.duckduckgo.app.browser.R @@ -33,6 +33,8 @@ import com.duckduckgo.common.ui.view.MessageCta.Message import com.duckduckgo.common.ui.view.gone import com.duckduckgo.common.ui.view.show import com.duckduckgo.common.ui.viewbinding.viewBinding +import com.duckduckgo.common.utils.ConflatedJob +import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.common.utils.ViewViewModelFactory import com.duckduckgo.di.scopes.AppScope import com.duckduckgo.di.scopes.ViewScope @@ -40,9 +42,7 @@ import com.duckduckgo.newtabpage.api.NewTabPageSection import com.duckduckgo.newtabpage.api.NewTabPageSectionPlugin import dagger.android.support.AndroidSupportInjection import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -56,7 +56,8 @@ class IndonesiaNewTabSectionView @JvmOverloads constructor( @Inject lateinit var viewModelFactory: ViewViewModelFactory - private var coroutineScope: CoroutineScope? = null + @Inject + lateinit var dispatchers: DispatcherProvider private val binding: ViewIndonesiaNewTabSectionBinding by viewBinding() @@ -64,18 +65,22 @@ class IndonesiaNewTabSectionView @JvmOverloads constructor( ViewModelProvider(findViewTreeViewModelStoreOwner()!!, viewModelFactory)[IndonesiaNewTabSectionViewModel::class.java] } + private val conflatedJob = ConflatedJob() + override fun onAttachedToWindow() { AndroidSupportInjection.inject(this) super.onAttachedToWindow() findViewTreeLifecycleOwner()?.lifecycle?.addObserver(viewModel) - @SuppressLint("NoHardcodedCoroutineDispatcher") - coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) - - viewModel.viewState + conflatedJob += viewModel.viewState .onEach { render(it) } - .launchIn(coroutineScope!!) + .launchIn(findViewTreeLifecycleOwner()?.lifecycleScope!!) + } + + override fun onDetachedFromWindow() { + conflatedJob.cancel() + super.onDetachedFromWindow() } private fun render(viewState: ViewState) { diff --git a/app/src/main/java/com/duckduckgo/app/browser/newtab/NewTabLegacyPageView.kt b/app/src/main/java/com/duckduckgo/app/browser/newtab/NewTabLegacyPageView.kt index 377570cd3cef..04cd5a025cff 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/newtab/NewTabLegacyPageView.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/newtab/NewTabLegacyPageView.kt @@ -16,7 +16,6 @@ package com.duckduckgo.app.browser.newtab -import android.annotation.SuppressLint import android.app.PendingIntent import android.content.ActivityNotFoundException import android.content.Context @@ -29,6 +28,7 @@ import androidx.core.view.isVisible import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.findViewTreeViewModelStoreOwner +import androidx.lifecycle.lifecycleScope import com.duckduckgo.anvil.annotations.InjectWith import com.duckduckgo.app.browser.HomeBackgroundLogo import com.duckduckgo.app.browser.databinding.ViewNewTabLegacyBinding @@ -50,6 +50,8 @@ import com.duckduckgo.app.tabs.BrowserNav import com.duckduckgo.common.ui.view.gone import com.duckduckgo.common.ui.view.show import com.duckduckgo.common.ui.viewbinding.viewBinding +import com.duckduckgo.common.utils.ConflatedJob +import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.common.utils.ViewViewModelFactory import com.duckduckgo.di.scopes.ViewScope import com.duckduckgo.mobile.android.app.tracking.ui.AppTrackingProtectionScreens.AppTrackerOnboardingActivityWithEmptyParamsParams @@ -58,9 +60,6 @@ import com.duckduckgo.navigation.api.GlobalActivityStarter.DeeplinkActivityParam import com.duckduckgo.remote.messaging.api.RemoteMessage import dagger.android.support.AndroidSupportInjection import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -88,7 +87,8 @@ class NewTabLegacyPageView @JvmOverloads constructor( @Inject lateinit var pixel: Pixel - private var coroutineScope: CoroutineScope? = null + @Inject + lateinit var dispatchers: DispatcherProvider private val binding: ViewNewTabLegacyBinding by viewBinding() @@ -98,30 +98,30 @@ class NewTabLegacyPageView @JvmOverloads constructor( ViewModelProvider(findViewTreeViewModelStoreOwner()!!, viewModelFactory)[NewTabLegacyPageViewModel::class.java] } + private val conflatedStateJob = ConflatedJob() + private val conflatedCommandJob = ConflatedJob() + override fun onAttachedToWindow() { AndroidSupportInjection.inject(this) super.onAttachedToWindow() findViewTreeLifecycleOwner()?.lifecycle?.addObserver(viewModel) - @SuppressLint("NoHardcodedCoroutineDispatcher") - coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) - - viewModel.viewState + conflatedStateJob += viewModel.viewState .onEach { render(it) } - .launchIn(coroutineScope!!) + .launchIn(findViewTreeLifecycleOwner()?.lifecycleScope!!) - viewModel.commands() + conflatedCommandJob += viewModel.commands() .onEach { processCommands(it) } - .launchIn(coroutineScope!!) + .launchIn(findViewTreeLifecycleOwner()?.lifecycleScope!!) } override fun onDetachedFromWindow() { super.onDetachedFromWindow() findViewTreeLifecycleOwner()?.lifecycle?.removeObserver(viewModel) - coroutineScope?.cancel() - coroutineScope = null + conflatedStateJob.cancel() + conflatedCommandJob.cancel() } private fun render(viewState: ViewState) { diff --git a/app/src/main/java/com/duckduckgo/app/browser/omnibar/OmnibarLayout.kt b/app/src/main/java/com/duckduckgo/app/browser/omnibar/OmnibarLayout.kt index 83f9e3a2c3ac..75bb513738f6 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/omnibar/OmnibarLayout.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/omnibar/OmnibarLayout.kt @@ -16,7 +16,6 @@ package com.duckduckgo.app.browser.omnibar -import android.annotation.SuppressLint import android.content.Context import android.graphics.Color import android.graphics.drawable.ColorDrawable @@ -38,6 +37,7 @@ import androidx.core.view.isVisible import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.findViewTreeViewModelStoreOwner +import androidx.lifecycle.lifecycleScope import com.airbnb.lottie.LottieAnimationView import com.duckduckgo.anvil.annotations.InjectWith import com.duckduckgo.app.browser.PulseAnimation @@ -75,6 +75,8 @@ import com.duckduckgo.common.ui.view.KeyboardAwareEditText.ShowSuggestionsListen import com.duckduckgo.common.ui.view.gone import com.duckduckgo.common.ui.view.hide import com.duckduckgo.common.ui.view.show +import com.duckduckgo.common.utils.ConflatedJob +import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.common.utils.FragmentViewModelFactory import com.duckduckgo.common.utils.extensions.replaceTextChangedListener import com.duckduckgo.common.utils.text.TextChangedWatcher @@ -82,9 +84,6 @@ import com.duckduckgo.di.scopes.FragmentScope import com.google.android.material.appbar.AppBarLayout import dagger.android.support.AndroidSupportInjection import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -137,6 +136,9 @@ class OmnibarLayout @JvmOverloads constructor( @Inject lateinit var pixel: Pixel + @Inject + lateinit var dispatchers: DispatcherProvider + private lateinit var pulseAnimation: PulseAnimation private var omnibarTextListener: Omnibar.TextListener? = null @@ -218,8 +220,6 @@ class OmnibarLayout @JvmOverloads constructor( } } - private var coroutineScope: CoroutineScope? = null - private val smoothProgressAnimator by lazy { SmoothProgressAnimator(pageLoadingIndicator) } private val viewModel: OmnibarLayoutViewModel by lazy { @@ -229,20 +229,22 @@ class OmnibarLayout @JvmOverloads constructor( )[OmnibarLayoutViewModel::class.java] } + private val conflatedStateJob = ConflatedJob() + private val conflatedCommandJob = ConflatedJob() + override fun onAttachedToWindow() { AndroidSupportInjection.inject(this) super.onAttachedToWindow() pulseAnimation = PulseAnimation(findViewTreeLifecycleOwner()!!) - @SuppressLint("NoHardcodedCoroutineDispatcher") - coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) + val coroutineScope = findViewTreeLifecycleOwner()?.lifecycleScope - viewModel.viewState + conflatedStateJob += viewModel.viewState .onEach { render(it) } .launchIn(coroutineScope!!) - viewModel.commands() + conflatedCommandJob += viewModel.commands() .onEach { processCommand(it) } .launchIn(coroutineScope!!) @@ -262,7 +264,8 @@ class OmnibarLayout @JvmOverloads constructor( } override fun onDetachedFromWindow() { - coroutineScope?.cancel() + conflatedStateJob.cancel() + conflatedCommandJob.cancel() super.onDetachedFromWindow() } diff --git a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/ui/credential/management/survey/SurveyInPasswordsPromotion.kt b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/ui/credential/management/survey/SurveyInPasswordsPromotion.kt index c9e702340eea..deb1701adf8e 100644 --- a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/ui/credential/management/survey/SurveyInPasswordsPromotion.kt +++ b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/impl/ui/credential/management/survey/SurveyInPasswordsPromotion.kt @@ -21,7 +21,9 @@ import android.util.AttributeSet import android.view.View import android.widget.FrameLayout import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.findViewTreeViewModelStoreOwner +import androidx.lifecycle.lifecycleScope import com.duckduckgo.anvil.annotations.InjectWith import com.duckduckgo.anvil.annotations.PriorityKey import com.duckduckgo.app.tabs.BrowserNav @@ -45,8 +47,6 @@ import com.duckduckgo.navigation.api.GlobalActivityStarter import com.squareup.anvil.annotations.ContributesMultibinding import dagger.android.support.AndroidSupportInjection import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -92,17 +92,15 @@ class SurveyInPasswordsPromotionView @JvmOverloads constructor( } private var job: ConflatedJob = ConflatedJob() - private lateinit var coroutineScope: CoroutineScope internal lateinit var survey: SurveyDetails override fun onAttachedToWindow() { AndroidSupportInjection.inject(this) - coroutineScope = CoroutineScope(SupervisorJob() + dispatchers.main()) super.onAttachedToWindow() job += viewModel.commands() .onEach { processCommand(it) } - .launchIn(coroutineScope) + .launchIn(findViewTreeLifecycleOwner()?.lifecycleScope!!) showSurvey(survey) @@ -111,7 +109,6 @@ class SurveyInPasswordsPromotionView @JvmOverloads constructor( override fun onDetachedFromWindow() { super.onDetachedFromWindow() - coroutineScope.cancel() job.cancel() } diff --git a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/sync/CredentialsSyncPausedView.kt b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/sync/CredentialsSyncPausedView.kt index 87b8c56faa33..fff1c651cb11 100644 --- a/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/sync/CredentialsSyncPausedView.kt +++ b/autofill/autofill-impl/src/main/java/com/duckduckgo/autofill/sync/CredentialsSyncPausedView.kt @@ -16,7 +16,6 @@ package com.duckduckgo.autofill.sync -import android.annotation.SuppressLint import android.content.Context import android.util.AttributeSet import android.widget.FrameLayout @@ -24,6 +23,7 @@ import androidx.core.view.isVisible import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.findViewTreeViewModelStoreOwner +import androidx.lifecycle.lifecycleScope import com.duckduckgo.anvil.annotations.InjectWith import com.duckduckgo.autofill.api.AutofillScreens.AutofillSettingsScreen import com.duckduckgo.autofill.api.AutofillSettingsLaunchSource @@ -33,14 +33,12 @@ import com.duckduckgo.autofill.sync.CredentialsSyncPausedViewModel.Command.Navig import com.duckduckgo.autofill.sync.CredentialsSyncPausedViewModel.ViewState import com.duckduckgo.common.ui.viewbinding.viewBinding import com.duckduckgo.common.utils.ConflatedJob +import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.common.utils.ViewViewModelFactory import com.duckduckgo.di.scopes.ViewScope import com.duckduckgo.navigation.api.GlobalActivityStarter import dagger.android.support.AndroidSupportInjection import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -58,9 +56,11 @@ class CredentialsSyncPausedView @JvmOverloads constructor( @Inject lateinit var viewModelFactory: ViewViewModelFactory - private var coroutineScope: CoroutineScope? = null + @Inject + lateinit var dispatchers: DispatcherProvider private var job: ConflatedJob = ConflatedJob() + private var conflatedStateJob: ConflatedJob = ConflatedJob() private val binding: ViewCredentialsSyncPausedWarningBinding by viewBinding() @@ -74,10 +74,9 @@ class CredentialsSyncPausedView @JvmOverloads constructor( findViewTreeLifecycleOwner()?.lifecycle?.addObserver(viewModel) - @SuppressLint("NoHardcodedCoroutineDispatcher") - coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) + val coroutineScope = findViewTreeLifecycleOwner()?.lifecycleScope - viewModel.viewState() + conflatedStateJob += viewModel.viewState() .onEach { render(it) } .launchIn(coroutineScope!!) @@ -89,9 +88,8 @@ class CredentialsSyncPausedView @JvmOverloads constructor( override fun onDetachedFromWindow() { super.onDetachedFromWindow() findViewTreeLifecycleOwner()?.lifecycle?.removeObserver(viewModel) - coroutineScope?.cancel() job.cancel() - coroutineScope = null + conflatedStateJob.cancel() } private fun processCommands(command: Command) { diff --git a/common/common-ui/src/main/java/com/duckduckgo/common/ui/notifyme/NotifyMeView.kt b/common/common-ui/src/main/java/com/duckduckgo/common/ui/notifyme/NotifyMeView.kt index b7872ce76481..93f2c4b7b1c1 100644 --- a/common/common-ui/src/main/java/com/duckduckgo/common/ui/notifyme/NotifyMeView.kt +++ b/common/common-ui/src/main/java/com/duckduckgo/common/ui/notifyme/NotifyMeView.kt @@ -38,6 +38,7 @@ import androidx.core.view.updateLayoutParams import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.findViewTreeViewModelStoreOwner +import androidx.lifecycle.lifecycleScope import com.duckduckgo.anvil.annotations.InjectWith import com.duckduckgo.common.ui.notifyme.NotifyMeView.Orientation.Center import com.duckduckgo.common.ui.notifyme.NotifyMeViewModel.Command @@ -51,6 +52,8 @@ import com.duckduckgo.common.ui.notifyme.NotifyMeViewModel.ViewState import com.duckduckgo.common.ui.view.gone import com.duckduckgo.common.ui.view.show import com.duckduckgo.common.ui.viewbinding.viewBinding +import com.duckduckgo.common.utils.ConflatedJob +import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.common.utils.ViewViewModelFactory import com.duckduckgo.di.scopes.ViewScope import com.duckduckgo.mobile.android.R @@ -59,9 +62,6 @@ import com.duckduckgo.mobile.android.databinding.ViewNotifyMeViewBinding import com.google.android.material.button.MaterialButton.ICON_GRAVITY_TEXT_START import dagger.android.support.AndroidSupportInjection import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -76,12 +76,14 @@ class NotifyMeView @JvmOverloads constructor( @Inject lateinit var viewModelFactory: ViewViewModelFactory + @Inject + lateinit var dispatchers: DispatcherProvider + private lateinit var sharedPrefsKeyForDismiss: String private var onNotifyMeButtonClicked: () -> Unit = {} private var onNotifyMeCloseButtonClicked: () -> Unit = {} - private var coroutineScope: CoroutineScope? = null private var vtoGlobalLayoutListener: OnGlobalLayoutListener? = null private var visibilityChangedListener: OnVisibilityChangedListener? = null @@ -91,6 +93,9 @@ class NotifyMeView @JvmOverloads constructor( ViewModelProvider(findViewTreeViewModelStoreOwner()!!, viewModelFactory)[NotifyMeViewModel::class.java] } + private val conflatedStateJob = ConflatedJob() + private val conflatedCommandJob = ConflatedJob() + init { val attributes = context.obtainStyledAttributes(attrs, R.styleable.NotifyMeView) setPrimaryText(attributes.getString(R.styleable.NotifyMeView_primaryText) ?: "") @@ -117,16 +122,15 @@ class NotifyMeView @JvmOverloads constructor( findViewTreeLifecycleOwner()?.lifecycle?.addObserver(viewModel) - @SuppressLint("NoHardcodedCoroutineDispatcher") - coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) + val coroutineScope = findViewTreeLifecycleOwner()?.lifecycleScope viewModel.init(sharedPrefsKeyForDismiss) - viewModel.viewState + conflatedStateJob += viewModel.viewState .onEach { render(it) } .launchIn(coroutineScope!!) - viewModel.commands() + conflatedCommandJob += viewModel.commands() .onEach { processCommands(it) } .launchIn(coroutineScope!!) @@ -140,8 +144,8 @@ class NotifyMeView @JvmOverloads constructor( findViewTreeLifecycleOwner()?.lifecycle?.removeObserver(viewModel) - coroutineScope?.cancel() - coroutineScope = null + conflatedStateJob.cancel() + conflatedCommandJob.cancel() } fun setOnVisibilityChange(visibilityChangedListener: OnVisibilityChangedListener) { diff --git a/network-protection/network-protection-impl/src/main/java/com/duckduckgo/networkprotection/impl/subscription/settings/LegacyProSettingNetPView.kt b/network-protection/network-protection-impl/src/main/java/com/duckduckgo/networkprotection/impl/subscription/settings/LegacyProSettingNetPView.kt index 99b2edc9d248..ba199dbdb844 100644 --- a/network-protection/network-protection-impl/src/main/java/com/duckduckgo/networkprotection/impl/subscription/settings/LegacyProSettingNetPView.kt +++ b/network-protection/network-protection-impl/src/main/java/com/duckduckgo/networkprotection/impl/subscription/settings/LegacyProSettingNetPView.kt @@ -16,17 +16,19 @@ package com.duckduckgo.networkprotection.impl.subscription.settings -import android.annotation.SuppressLint import android.content.Context import android.util.AttributeSet import android.widget.FrameLayout import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.findViewTreeViewModelStoreOwner +import androidx.lifecycle.lifecycleScope import com.duckduckgo.anvil.annotations.InjectWith import com.duckduckgo.common.ui.view.gone import com.duckduckgo.common.ui.view.show import com.duckduckgo.common.ui.viewbinding.viewBinding +import com.duckduckgo.common.utils.ConflatedJob +import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.di.scopes.ViewScope import com.duckduckgo.mobile.android.R as CommonR import com.duckduckgo.navigation.api.GlobalActivityStarter @@ -40,9 +42,6 @@ import com.duckduckgo.networkprotection.impl.subscription.settings.LegacyProSett import com.duckduckgo.networkprotection.impl.subscription.settings.LegacyProSettingNetPViewModel.NetPEntryState.ShowState import dagger.android.support.AndroidSupportInjection import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -60,7 +59,8 @@ class LegacyProSettingNetPView @JvmOverloads constructor( @Inject lateinit var globalActivityStarter: GlobalActivityStarter - private var coroutineScope: CoroutineScope? = null + @Inject + lateinit var dispatchers: DispatcherProvider private val binding: LegacyViewSettingsNetpBinding by viewBinding() @@ -68,6 +68,9 @@ class LegacyProSettingNetPView @JvmOverloads constructor( ViewModelProvider(findViewTreeViewModelStoreOwner()!!, viewModelFactory)[LegacyProSettingNetPViewModel::class.java] } + private val conflatedStateJob = ConflatedJob() + private val conflatedCommandJob = ConflatedJob() + override fun onAttachedToWindow() { AndroidSupportInjection.inject(this) super.onAttachedToWindow() @@ -78,14 +81,13 @@ class LegacyProSettingNetPView @JvmOverloads constructor( viewModel.onNetPSettingClicked() } - @SuppressLint("NoHardcodedCoroutineDispatcher") - coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) + val coroutineScope = findViewTreeLifecycleOwner()?.lifecycleScope - viewModel.viewState + conflatedStateJob += viewModel.viewState .onEach { updateNetPSettings(it.networkProtectionEntryState) } .launchIn(coroutineScope!!) - viewModel.commands() + conflatedCommandJob += viewModel.commands() .onEach { processCommands(it) } .launchIn(coroutineScope!!) } @@ -109,8 +111,8 @@ class LegacyProSettingNetPView @JvmOverloads constructor( override fun onDetachedFromWindow() { super.onDetachedFromWindow() findViewTreeLifecycleOwner()?.lifecycle?.removeObserver(viewModel) - coroutineScope?.cancel() - coroutineScope = null + conflatedStateJob.cancel() + conflatedCommandJob.cancel() } private fun processCommands(command: Command) { diff --git a/network-protection/network-protection-impl/src/main/java/com/duckduckgo/networkprotection/impl/subscription/settings/ProSettingNetPView.kt b/network-protection/network-protection-impl/src/main/java/com/duckduckgo/networkprotection/impl/subscription/settings/ProSettingNetPView.kt index 2ff901da3050..516855c79e0b 100644 --- a/network-protection/network-protection-impl/src/main/java/com/duckduckgo/networkprotection/impl/subscription/settings/ProSettingNetPView.kt +++ b/network-protection/network-protection-impl/src/main/java/com/duckduckgo/networkprotection/impl/subscription/settings/ProSettingNetPView.kt @@ -16,7 +16,6 @@ package com.duckduckgo.networkprotection.impl.subscription.settings -import android.annotation.SuppressLint import android.content.Context import android.util.AttributeSet import android.widget.FrameLayout @@ -25,8 +24,11 @@ import androidx.core.view.isVisible import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.findViewTreeViewModelStoreOwner +import androidx.lifecycle.lifecycleScope import com.duckduckgo.anvil.annotations.InjectWith import com.duckduckgo.common.ui.viewbinding.viewBinding +import com.duckduckgo.common.utils.ConflatedJob +import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.di.scopes.ViewScope import com.duckduckgo.navigation.api.GlobalActivityStarter import com.duckduckgo.networkprotection.impl.R @@ -40,9 +42,6 @@ import com.duckduckgo.networkprotection.impl.subscription.settings.ProSettingNet import com.duckduckgo.networkprotection.impl.subscription.settings.ProSettingNetPViewModel.NetPEntryState.Hidden import dagger.android.support.AndroidSupportInjection import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -60,13 +59,16 @@ class ProSettingNetPView @JvmOverloads constructor( @Inject lateinit var globalActivityStarter: GlobalActivityStarter - private var coroutineScope: CoroutineScope? = null + @Inject + lateinit var dispatchers: DispatcherProvider private val binding: ViewSettingsNetpBinding by viewBinding() private val viewModel: ProSettingNetPViewModel by lazy { ViewModelProvider(findViewTreeViewModelStoreOwner()!!, viewModelFactory)[ProSettingNetPViewModel::class.java] } + private val conflatedStateJob = ConflatedJob() + private val conflatedCommandJob = ConflatedJob() override fun onAttachedToWindow() { AndroidSupportInjection.inject(this) @@ -74,14 +76,13 @@ class ProSettingNetPView @JvmOverloads constructor( findViewTreeLifecycleOwner()?.lifecycle?.addObserver(viewModel) - @SuppressLint("NoHardcodedCoroutineDispatcher") - coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) + val coroutineScope = findViewTreeLifecycleOwner()?.lifecycleScope - viewModel.viewState + conflatedStateJob += viewModel.viewState .onEach { updateNetPSettings(it.netPEntryState) } .launchIn(coroutineScope!!) - viewModel.commands() + conflatedCommandJob += viewModel.commands() .onEach { processCommands(it) } .launchIn(coroutineScope!!) } @@ -111,8 +112,8 @@ class ProSettingNetPView @JvmOverloads constructor( override fun onDetachedFromWindow() { super.onDetachedFromWindow() findViewTreeLifecycleOwner()?.lifecycle?.removeObserver(viewModel) - coroutineScope?.cancel() - coroutineScope = null + conflatedCommandJob.cancel() + conflatedStateJob.cancel() } private fun processCommands(command: Command) { diff --git a/new-tab-page/new-tab-page-impl/src/main/java/com/duckduckgo/newtabpage/impl/shortcuts/ShortcutsNewTabSectionView.kt b/new-tab-page/new-tab-page-impl/src/main/java/com/duckduckgo/newtabpage/impl/shortcuts/ShortcutsNewTabSectionView.kt index 160ad9b16dc4..f3f448309634 100644 --- a/new-tab-page/new-tab-page-impl/src/main/java/com/duckduckgo/newtabpage/impl/shortcuts/ShortcutsNewTabSectionView.kt +++ b/new-tab-page/new-tab-page-impl/src/main/java/com/duckduckgo/newtabpage/impl/shortcuts/ShortcutsNewTabSectionView.kt @@ -16,7 +16,6 @@ package com.duckduckgo.newtabpage.impl.shortcuts -import android.annotation.SuppressLint import android.content.Context import android.content.res.Configuration import android.util.AttributeSet @@ -25,6 +24,7 @@ import android.widget.LinearLayout import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.findViewTreeViewModelStoreOwner +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView @@ -34,6 +34,8 @@ import com.duckduckgo.common.ui.recyclerviewext.GridColumnCalculator import com.duckduckgo.common.ui.recyclerviewext.disableAnimation import com.duckduckgo.common.ui.recyclerviewext.enableAnimation import com.duckduckgo.common.ui.viewbinding.viewBinding +import com.duckduckgo.common.utils.ConflatedJob +import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.common.utils.ViewViewModelFactory import com.duckduckgo.di.scopes.AppScope import com.duckduckgo.di.scopes.ViewScope @@ -46,9 +48,6 @@ import com.duckduckgo.newtabpage.impl.shortcuts.ShortcutsAdapter.Companion.SHORT import com.duckduckgo.newtabpage.impl.shortcuts.ShortcutsViewModel.ViewState import dagger.android.support.AndroidSupportInjection import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -65,31 +64,36 @@ class ShortcutsNewTabSectionView @JvmOverloads constructor( @Inject lateinit var globalActivityStarter: GlobalActivityStarter + @Inject + lateinit var dispatchers: DispatcherProvider + private val binding: ViewNewTabShortcutsSectionBinding by viewBinding() private lateinit var adapter: ShortcutsAdapter private lateinit var itemTouchHelper: ItemTouchHelper - private var coroutineScope: CoroutineScope? = null - private val viewModel: ShortcutsViewModel by lazy { ViewModelProvider(findViewTreeViewModelStoreOwner()!!, viewModelFactory)[ShortcutsViewModel::class.java] } + private val conflatedJob = ConflatedJob() + override fun onAttachedToWindow() { AndroidSupportInjection.inject(this) super.onAttachedToWindow() findViewTreeLifecycleOwner()?.lifecycle?.addObserver(viewModel) - @SuppressLint("NoHardcodedCoroutineDispatcher") - coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) - configureGrid() - viewModel.viewState + conflatedJob += viewModel.viewState .onEach { render(it) } - .launchIn(coroutineScope!!) + .launchIn(findViewTreeLifecycleOwner()?.lifecycleScope!!) + } + + override fun onDetachedFromWindow() { + conflatedJob.cancel() + super.onDetachedFromWindow() } private fun render(viewState: ViewState) { diff --git a/new-tab-page/new-tab-page-impl/src/main/java/com/duckduckgo/newtabpage/impl/shortcuts/ShortcutsNewTabSettingView.kt b/new-tab-page/new-tab-page-impl/src/main/java/com/duckduckgo/newtabpage/impl/shortcuts/ShortcutsNewTabSettingView.kt index c4e1032e1ee2..7d10fccdd00e 100644 --- a/new-tab-page/new-tab-page-impl/src/main/java/com/duckduckgo/newtabpage/impl/shortcuts/ShortcutsNewTabSettingView.kt +++ b/new-tab-page/new-tab-page-impl/src/main/java/com/duckduckgo/newtabpage/impl/shortcuts/ShortcutsNewTabSettingView.kt @@ -16,7 +16,6 @@ package com.duckduckgo.newtabpage.impl.shortcuts -import android.annotation.SuppressLint import android.content.Context import android.util.AttributeSet import android.view.View @@ -24,9 +23,12 @@ import android.widget.LinearLayout import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.findViewTreeViewModelStoreOwner +import androidx.lifecycle.lifecycleScope import com.duckduckgo.anvil.annotations.InjectWith import com.duckduckgo.anvil.annotations.PriorityKey import com.duckduckgo.common.ui.viewbinding.viewBinding +import com.duckduckgo.common.utils.ConflatedJob +import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.common.utils.ViewViewModelFactory import com.duckduckgo.di.scopes.ActivityScope import com.duckduckgo.di.scopes.ViewScope @@ -37,9 +39,7 @@ import com.duckduckgo.newtabpage.impl.shortcuts.ShortcutsNewTabSettingsViewModel import com.squareup.anvil.annotations.ContributesMultibinding import dagger.android.support.AndroidSupportInjection import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -53,26 +53,31 @@ class ShortcutsNewTabSettingView @JvmOverloads constructor( @Inject lateinit var viewModelFactory: ViewViewModelFactory - private val binding: ViewNewTabShortcutsSettingItemBinding by viewBinding() + @Inject + lateinit var dispatchers: DispatcherProvider - private var coroutineScope: CoroutineScope? = null + private val binding: ViewNewTabShortcutsSettingItemBinding by viewBinding() private val viewModel: ShortcutsNewTabSettingsViewModel by lazy { ViewModelProvider(findViewTreeViewModelStoreOwner()!!, viewModelFactory)[ShortcutsNewTabSettingsViewModel::class.java] } + private val conflatedJob = ConflatedJob() + override fun onAttachedToWindow() { AndroidSupportInjection.inject(this) super.onAttachedToWindow() findViewTreeLifecycleOwner()?.lifecycle?.addObserver(viewModel) - @SuppressLint("NoHardcodedCoroutineDispatcher") - coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) - - viewModel.viewState + conflatedJob += viewModel.viewState .onEach { render(it) } - .launchIn(coroutineScope!!) + .launchIn(findViewTreeLifecycleOwner()?.lifecycleScope!!) + } + + override fun onDetachedFromWindow() { + conflatedJob.cancel() + super.onDetachedFromWindow() } private fun render(viewState: ViewState) { diff --git a/new-tab-page/new-tab-page-impl/src/main/java/com/duckduckgo/newtabpage/impl/view/NewTabPageView.kt b/new-tab-page/new-tab-page-impl/src/main/java/com/duckduckgo/newtabpage/impl/view/NewTabPageView.kt index 21a6e2b5b704..6973a2cea604 100644 --- a/new-tab-page/new-tab-page-impl/src/main/java/com/duckduckgo/newtabpage/impl/view/NewTabPageView.kt +++ b/new-tab-page/new-tab-page-impl/src/main/java/com/duckduckgo/newtabpage/impl/view/NewTabPageView.kt @@ -16,7 +16,6 @@ package com.duckduckgo.newtabpage.impl.view -import android.annotation.SuppressLint import android.content.Context import android.graphics.Rect import android.util.AttributeSet @@ -26,6 +25,7 @@ import androidx.core.view.children import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.findViewTreeViewModelStoreOwner +import androidx.lifecycle.lifecycleScope import com.duckduckgo.anvil.annotations.InjectWith import com.duckduckgo.browser.api.ui.BrowserScreens.NewTabSettingsScreenNoParams import com.duckduckgo.common.ui.view.gone @@ -33,6 +33,8 @@ import com.duckduckgo.common.ui.view.hide import com.duckduckgo.common.ui.view.hideKeyboard import com.duckduckgo.common.ui.view.show import com.duckduckgo.common.ui.viewbinding.viewBinding +import com.duckduckgo.common.utils.ConflatedJob +import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.common.utils.ViewViewModelFactory import com.duckduckgo.di.scopes.ViewScope import com.duckduckgo.navigation.api.GlobalActivityStarter @@ -41,9 +43,7 @@ import com.duckduckgo.newtabpage.impl.databinding.ViewNewTabPageBinding import com.duckduckgo.newtabpage.impl.view.NewTabPageViewModel.ViewState import dagger.android.support.AndroidSupportInjection import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import logcat.logcat @@ -61,7 +61,8 @@ class NewTabPageView @JvmOverloads constructor( @Inject lateinit var globalActivityStarter: GlobalActivityStarter - private var coroutineScope: CoroutineScope? = null + @Inject + lateinit var dispatchers: DispatcherProvider private val binding: ViewNewTabPageBinding by viewBinding() @@ -69,23 +70,27 @@ class NewTabPageView @JvmOverloads constructor( ViewModelProvider(findViewTreeViewModelStoreOwner()!!, viewModelFactory)[NewTabPageViewModel::class.java] } + private val conflatedJob = ConflatedJob() + override fun onAttachedToWindow() { AndroidSupportInjection.inject(this) super.onAttachedToWindow() findViewTreeLifecycleOwner()?.lifecycle?.addObserver(viewModel) - @SuppressLint("NoHardcodedCoroutineDispatcher") - coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) - - viewModel.viewState + conflatedJob += viewModel.viewState .onEach { render(it) } - .launchIn(coroutineScope!!) + .launchIn(findViewTreeLifecycleOwner()?.lifecycleScope!!) setClickListeners() setAnimationListeners() } + override fun onDetachedFromWindow() { + conflatedJob.cancel() + super.onDetachedFromWindow() + } + private fun render(viewState: ViewState) { logcat { "New Tab Render: loading: ${viewState.loading} showDax: ${viewState.showDax} sections: ${viewState.sections.size}" } if (viewState.loading) { diff --git a/remote-messaging/remote-messaging-impl/src/main/java/com/duckduckgo/remote/messaging/impl/newtab/RemoteMessageView.kt b/remote-messaging/remote-messaging-impl/src/main/java/com/duckduckgo/remote/messaging/impl/newtab/RemoteMessageView.kt index 79ea808fc9c3..62c444c980c8 100644 --- a/remote-messaging/remote-messaging-impl/src/main/java/com/duckduckgo/remote/messaging/impl/newtab/RemoteMessageView.kt +++ b/remote-messaging/remote-messaging-impl/src/main/java/com/duckduckgo/remote/messaging/impl/newtab/RemoteMessageView.kt @@ -16,7 +16,6 @@ package com.duckduckgo.remote.messaging.impl.newtab -import android.annotation.SuppressLint import android.app.PendingIntent import android.content.ActivityNotFoundException import android.content.Context @@ -30,12 +29,15 @@ import androidx.core.os.bundleOf import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.findViewTreeViewModelStoreOwner +import androidx.lifecycle.lifecycleScope import com.duckduckgo.anvil.annotations.ContributesActivePlugin import com.duckduckgo.anvil.annotations.InjectWith import com.duckduckgo.app.tabs.BrowserNav import com.duckduckgo.common.ui.view.gone import com.duckduckgo.common.ui.view.show import com.duckduckgo.common.ui.viewbinding.viewBinding +import com.duckduckgo.common.utils.ConflatedJob +import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.common.utils.ViewViewModelFactory import com.duckduckgo.di.scopes.AppScope import com.duckduckgo.di.scopes.ViewScope @@ -60,9 +62,7 @@ import com.duckduckgo.remote.messaging.impl.newtab.RemoteMessageViewModel.Comman import com.duckduckgo.remote.messaging.impl.newtab.RemoteMessageViewModel.ViewState import dagger.android.support.AndroidSupportInjection import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import timber.log.Timber @@ -83,32 +83,41 @@ class RemoteMessageView @JvmOverloads constructor( @Inject lateinit var browserNav: BrowserNav - private val binding: ViewRemoteMessageBinding by viewBinding() + @Inject + lateinit var dispatchers: DispatcherProvider - private var coroutineScope: CoroutineScope? = null + private val binding: ViewRemoteMessageBinding by viewBinding() private val viewModel: RemoteMessageViewModel by lazy { ViewModelProvider(findViewTreeViewModelStoreOwner()!!, viewModelFactory)[RemoteMessageViewModel::class.java] } + private val conflatedStateJob = ConflatedJob() + private val conflatedCommandJob = ConflatedJob() + override fun onAttachedToWindow() { AndroidSupportInjection.inject(this) super.onAttachedToWindow() findViewTreeLifecycleOwner()?.lifecycle?.addObserver(viewModel) - @SuppressLint("NoHardcodedCoroutineDispatcher") - coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) + val coroutineScope = findViewTreeLifecycleOwner()?.lifecycleScope - viewModel.viewState + conflatedStateJob += viewModel.viewState .onEach { render(it) } .launchIn(coroutineScope!!) - viewModel.commands() + conflatedCommandJob += viewModel.commands() .onEach { processCommands(it) } .launchIn(coroutineScope!!) } + override fun onDetachedFromWindow() { + conflatedStateJob.cancel() + conflatedCommandJob.cancel() + super.onDetachedFromWindow() + } + private fun render(viewState: ViewState) { if (viewState.message != null) { showRemoteMessage(viewState.message, viewState.newMessage) diff --git a/saved-sites/saved-sites-impl/src/main/java/com/duckduckgo/savedsites/impl/newtab/FavouritesNewTabSectionView.kt b/saved-sites/saved-sites-impl/src/main/java/com/duckduckgo/savedsites/impl/newtab/FavouritesNewTabSectionView.kt index 80742aed9167..b1c8b976c48d 100644 --- a/saved-sites/saved-sites-impl/src/main/java/com/duckduckgo/savedsites/impl/newtab/FavouritesNewTabSectionView.kt +++ b/saved-sites/saved-sites-impl/src/main/java/com/duckduckgo/savedsites/impl/newtab/FavouritesNewTabSectionView.kt @@ -17,7 +17,6 @@ package com.duckduckgo.savedsites.impl.newtab import android.animation.ValueAnimator -import android.annotation.SuppressLint import android.content.Context import android.content.res.Configuration import android.text.Spanned @@ -32,6 +31,7 @@ import androidx.fragment.app.FragmentManager import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.findViewTreeViewModelStoreOwner +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView @@ -49,6 +49,8 @@ import com.duckduckgo.common.ui.view.makeSnackbarWithNoBottomInset import com.duckduckgo.common.ui.view.show import com.duckduckgo.common.ui.view.toPx import com.duckduckgo.common.ui.viewbinding.viewBinding +import com.duckduckgo.common.utils.ConflatedJob +import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.common.utils.ViewViewModelFactory import com.duckduckgo.common.utils.extensions.html import com.duckduckgo.di.scopes.AppScope @@ -80,9 +82,7 @@ import com.google.android.material.snackbar.BaseTransientBottomBar import com.google.android.material.snackbar.Snackbar import dagger.android.support.AndroidSupportInjection import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -102,7 +102,8 @@ class FavouritesNewTabSectionView @JvmOverloads constructor( @Inject lateinit var browserNav: BrowserNav - private var coroutineScope: CoroutineScope? = null + @Inject + lateinit var dispatchers: DispatcherProvider private var isExpandable = true private var showPlaceholders = false @@ -125,6 +126,9 @@ class FavouritesNewTabSectionView @JvmOverloads constructor( } } + private val conflatedStateJob = ConflatedJob() + private val conflatedCommandJob = ConflatedJob() + init { context.obtainStyledAttributes( attrs, @@ -145,18 +149,23 @@ class FavouritesNewTabSectionView @JvmOverloads constructor( findViewTreeLifecycleOwner()?.lifecycle?.addObserver(viewModel) - @SuppressLint("NoHardcodedCoroutineDispatcher") - coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) + val coroutineScope = findViewTreeLifecycleOwner()?.lifecycleScope + + configureViews() viewModel.viewState .onEach { render(it) } .launchIn(coroutineScope!!) - viewModel.commands() + conflatedCommandJob += viewModel.commands() .onEach { processCommands(it) } .launchIn(coroutineScope!!) + } - configureViews() + override fun onDetachedFromWindow() { + conflatedStateJob.cancel() + conflatedCommandJob.cancel() + super.onDetachedFromWindow() } private fun configureViews() { diff --git a/saved-sites/saved-sites-impl/src/main/java/com/duckduckgo/savedsites/impl/newtab/FavouritesNewTabSettingView.kt b/saved-sites/saved-sites-impl/src/main/java/com/duckduckgo/savedsites/impl/newtab/FavouritesNewTabSettingView.kt index 225c2c94cab4..23bf99b5b8df 100644 --- a/saved-sites/saved-sites-impl/src/main/java/com/duckduckgo/savedsites/impl/newtab/FavouritesNewTabSettingView.kt +++ b/saved-sites/saved-sites-impl/src/main/java/com/duckduckgo/savedsites/impl/newtab/FavouritesNewTabSettingView.kt @@ -16,7 +16,6 @@ package com.duckduckgo.savedsites.impl.newtab -import android.annotation.SuppressLint import android.content.Context import android.util.AttributeSet import android.view.View @@ -24,10 +23,13 @@ import android.widget.LinearLayout import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.findViewTreeViewModelStoreOwner +import androidx.lifecycle.lifecycleScope import com.duckduckgo.anvil.annotations.ContributesRemoteFeature import com.duckduckgo.anvil.annotations.InjectWith import com.duckduckgo.anvil.annotations.PriorityKey import com.duckduckgo.common.ui.viewbinding.viewBinding +import com.duckduckgo.common.utils.ConflatedJob +import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.common.utils.ViewViewModelFactory import com.duckduckgo.di.scopes.ActivityScope import com.duckduckgo.di.scopes.AppScope @@ -40,9 +42,7 @@ import com.duckduckgo.savedsites.impl.newtab.FavouritesNewTabSettingsViewModel.V import com.squareup.anvil.annotations.ContributesMultibinding import dagger.android.support.AndroidSupportInjection import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -56,26 +56,31 @@ class FavouritesNewTabSettingView @JvmOverloads constructor( @Inject lateinit var viewModelFactory: ViewViewModelFactory - private val binding: ViewFavouritesSettingsItemBinding by viewBinding() + @Inject + lateinit var dispatchers: DispatcherProvider - private var coroutineScope: CoroutineScope? = null + private val binding: ViewFavouritesSettingsItemBinding by viewBinding() private val viewModel: FavouritesNewTabSettingsViewModel by lazy { ViewModelProvider(findViewTreeViewModelStoreOwner()!!, viewModelFactory)[FavouritesNewTabSettingsViewModel::class.java] } + private val conflatedJob = ConflatedJob() + override fun onAttachedToWindow() { AndroidSupportInjection.inject(this) super.onAttachedToWindow() findViewTreeLifecycleOwner()?.lifecycle?.addObserver(viewModel) - @SuppressLint("NoHardcodedCoroutineDispatcher") - coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) - - viewModel.viewState + conflatedJob += viewModel.viewState .onEach { render(it) } - .launchIn(coroutineScope!!) + .launchIn(findViewTreeLifecycleOwner()?.lifecycleScope!!) + } + + override fun onDetachedFromWindow() { + conflatedJob.cancel() + super.onDetachedFromWindow() } private fun render(viewState: ViewState) { diff --git a/saved-sites/saved-sites-impl/src/main/java/com/duckduckgo/savedsites/impl/sync/DisplayModeSyncSetting.kt b/saved-sites/saved-sites-impl/src/main/java/com/duckduckgo/savedsites/impl/sync/DisplayModeSyncSetting.kt index cd61caec41c2..ec939de2b552 100644 --- a/saved-sites/saved-sites-impl/src/main/java/com/duckduckgo/savedsites/impl/sync/DisplayModeSyncSetting.kt +++ b/saved-sites/saved-sites-impl/src/main/java/com/duckduckgo/savedsites/impl/sync/DisplayModeSyncSetting.kt @@ -16,7 +16,6 @@ package com.duckduckgo.savedsites.impl.sync -import android.annotation.SuppressLint import android.content.* import android.util.* import android.widget.* @@ -25,14 +24,12 @@ import androidx.lifecycle.* import com.duckduckgo.anvil.annotations.* import com.duckduckgo.common.ui.viewbinding.viewBinding import com.duckduckgo.common.utils.ConflatedJob +import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.di.scopes.* import com.duckduckgo.saved.sites.impl.databinding.* import com.duckduckgo.savedsites.impl.sync.DisplayModeViewModel.ViewState import dagger.android.support.* import javax.inject.* -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -47,7 +44,8 @@ class DisplayModeSyncSetting @JvmOverloads constructor( @Inject lateinit var viewModelFactory: DisplayModeViewModel.Factory - private var coroutineScope: CoroutineScope? = null + @Inject + lateinit var dispatchers: DispatcherProvider private var job: ConflatedJob = ConflatedJob() @@ -69,19 +67,14 @@ class DisplayModeSyncSetting @JvmOverloads constructor( }, ) - @SuppressLint("NoHardcodedCoroutineDispatcher") - coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) - job += viewModel.viewState() .onEach { render(it) } - .launchIn(coroutineScope!!) + .launchIn(findViewTreeLifecycleOwner()?.lifecycleScope!!) } override fun onDetachedFromWindow() { super.onDetachedFromWindow() - coroutineScope?.cancel() job.cancel() - coroutineScope = null } private fun render(it: ViewState) { diff --git a/saved-sites/saved-sites-impl/src/main/java/com/duckduckgo/savedsites/impl/sync/SavedSiteInvalidItemsView.kt b/saved-sites/saved-sites-impl/src/main/java/com/duckduckgo/savedsites/impl/sync/SavedSiteInvalidItemsView.kt index d20fb7d55b3a..59af743f49ad 100644 --- a/saved-sites/saved-sites-impl/src/main/java/com/duckduckgo/savedsites/impl/sync/SavedSiteInvalidItemsView.kt +++ b/saved-sites/saved-sites-impl/src/main/java/com/duckduckgo/savedsites/impl/sync/SavedSiteInvalidItemsView.kt @@ -16,7 +16,6 @@ package com.duckduckgo.savedsites.impl.sync -import android.annotation.SuppressLint import android.content.Context import android.text.SpannableStringBuilder import android.util.AttributeSet @@ -25,10 +24,12 @@ import androidx.core.view.isVisible import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.findViewTreeViewModelStoreOwner +import androidx.lifecycle.lifecycleScope import com.duckduckgo.anvil.annotations.InjectWith import com.duckduckgo.browser.api.ui.BrowserScreens.BookmarksScreenNoParams import com.duckduckgo.common.ui.viewbinding.viewBinding import com.duckduckgo.common.utils.ConflatedJob +import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.common.utils.ViewViewModelFactory import com.duckduckgo.di.scopes.ViewScope import com.duckduckgo.navigation.api.GlobalActivityStarter @@ -39,9 +40,6 @@ import com.duckduckgo.savedsites.impl.sync.SavedSiteInvalidItemsViewModel.Comman import com.duckduckgo.savedsites.impl.sync.SavedSiteInvalidItemsViewModel.ViewState import dagger.android.support.AndroidSupportInjection import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -59,9 +57,11 @@ class SavedSiteInvalidItemsView @JvmOverloads constructor( @Inject lateinit var viewModelFactory: ViewViewModelFactory - private var coroutineScope: CoroutineScope? = null + @Inject + lateinit var dispatchers: DispatcherProvider private var job: ConflatedJob = ConflatedJob() + private var conflatedStateJob: ConflatedJob = ConflatedJob() private val binding: ViewSaveSiteSyncInvalidItemsWarningBinding by viewBinding() @@ -75,10 +75,9 @@ class SavedSiteInvalidItemsView @JvmOverloads constructor( findViewTreeLifecycleOwner()?.lifecycle?.addObserver(viewModel) - @SuppressLint("NoHardcodedCoroutineDispatcher") - coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) + val coroutineScope = findViewTreeLifecycleOwner()?.lifecycleScope - viewModel.viewState() + conflatedStateJob += viewModel.viewState() .onEach { render(it) } .launchIn(coroutineScope!!) @@ -90,9 +89,8 @@ class SavedSiteInvalidItemsView @JvmOverloads constructor( override fun onDetachedFromWindow() { super.onDetachedFromWindow() findViewTreeLifecycleOwner()?.lifecycle?.removeObserver(viewModel) - coroutineScope?.cancel() job.cancel() - coroutineScope = null + conflatedStateJob.cancel() } private fun processCommands(command: Command) { diff --git a/saved-sites/saved-sites-impl/src/main/java/com/duckduckgo/savedsites/impl/sync/SavedSiteSyncPausedView.kt b/saved-sites/saved-sites-impl/src/main/java/com/duckduckgo/savedsites/impl/sync/SavedSiteSyncPausedView.kt index e10f7cc09657..644882c6264b 100644 --- a/saved-sites/saved-sites-impl/src/main/java/com/duckduckgo/savedsites/impl/sync/SavedSiteSyncPausedView.kt +++ b/saved-sites/saved-sites-impl/src/main/java/com/duckduckgo/savedsites/impl/sync/SavedSiteSyncPausedView.kt @@ -16,7 +16,6 @@ package com.duckduckgo.savedsites.impl.sync -import android.annotation.SuppressLint import android.content.Context import android.util.AttributeSet import android.widget.FrameLayout @@ -24,10 +23,12 @@ import androidx.core.view.isVisible import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.findViewTreeViewModelStoreOwner +import androidx.lifecycle.lifecycleScope import com.duckduckgo.anvil.annotations.InjectWith import com.duckduckgo.browser.api.ui.BrowserScreens.BookmarksScreenNoParams import com.duckduckgo.common.ui.viewbinding.viewBinding import com.duckduckgo.common.utils.ConflatedJob +import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.common.utils.ViewViewModelFactory import com.duckduckgo.di.scopes.ViewScope import com.duckduckgo.navigation.api.GlobalActivityStarter @@ -37,9 +38,6 @@ import com.duckduckgo.savedsites.impl.sync.SavedSiteSyncPausedViewModel.Command. import com.duckduckgo.savedsites.impl.sync.SavedSiteSyncPausedViewModel.ViewState import dagger.android.support.AndroidSupportInjection import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -57,9 +55,11 @@ class SavedSiteSyncPausedView @JvmOverloads constructor( @Inject lateinit var viewModelFactory: ViewViewModelFactory - private var coroutineScope: CoroutineScope? = null + @Inject + lateinit var dispatchers: DispatcherProvider private var job: ConflatedJob = ConflatedJob() + private var conflatedStateJob: ConflatedJob = ConflatedJob() private val binding: ViewSaveSiteSyncPausedWarningBinding by viewBinding() @@ -73,10 +73,9 @@ class SavedSiteSyncPausedView @JvmOverloads constructor( findViewTreeLifecycleOwner()?.lifecycle?.addObserver(viewModel) - @SuppressLint("NoHardcodedCoroutineDispatcher") - coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) + val coroutineScope = findViewTreeLifecycleOwner()?.lifecycleScope - viewModel.viewState() + conflatedStateJob += viewModel.viewState() .onEach { render(it) } .launchIn(coroutineScope!!) @@ -88,9 +87,8 @@ class SavedSiteSyncPausedView @JvmOverloads constructor( override fun onDetachedFromWindow() { super.onDetachedFromWindow() findViewTreeLifecycleOwner()?.lifecycle?.removeObserver(viewModel) - coroutineScope?.cancel() job.cancel() - coroutineScope = null + conflatedStateJob.cancel() } private fun processCommands(command: Command) { diff --git a/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/ItrSettingView.kt b/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/ItrSettingView.kt index 8dd9209930eb..13ab7853514a 100644 --- a/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/ItrSettingView.kt +++ b/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/ItrSettingView.kt @@ -16,7 +16,6 @@ package com.duckduckgo.subscriptions.impl.settings.views -import android.annotation.SuppressLint import android.content.Context import android.util.AttributeSet import android.widget.FrameLayout @@ -25,9 +24,11 @@ import androidx.core.view.isVisible import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.findViewTreeViewModelStoreOwner +import androidx.lifecycle.lifecycleScope import com.duckduckgo.anvil.annotations.InjectWith import com.duckduckgo.common.ui.viewbinding.viewBinding import com.duckduckgo.common.utils.ConflatedJob +import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.common.utils.ViewViewModelFactory import com.duckduckgo.di.scopes.ViewScope import com.duckduckgo.navigation.api.GlobalActivityStarter @@ -41,9 +42,6 @@ import com.duckduckgo.subscriptions.impl.settings.views.ItrSettingViewModel.View import com.duckduckgo.subscriptions.impl.ui.SubscriptionsWebViewActivityWithParams import dagger.android.support.AndroidSupportInjection import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -61,7 +59,8 @@ class ItrSettingView @JvmOverloads constructor( @Inject lateinit var globalActivityStarter: GlobalActivityStarter - private var coroutineScope: CoroutineScope? = null + @Inject + lateinit var dispatchers: DispatcherProvider private val binding: ViewItrSettingsBinding by viewBinding() @@ -70,21 +69,20 @@ class ItrSettingView @JvmOverloads constructor( } private var job: ConflatedJob = ConflatedJob() + private var conflatedStateJob: ConflatedJob = ConflatedJob() override fun onAttachedToWindow() { AndroidSupportInjection.inject(this) super.onAttachedToWindow() findViewTreeLifecycleOwner()?.lifecycle?.addObserver(viewModel) - - @SuppressLint("NoHardcodedCoroutineDispatcher") - coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) + val coroutineScope = findViewTreeLifecycleOwner()?.lifecycleScope job += viewModel.commands() .onEach { processCommands(it) } .launchIn(coroutineScope!!) - viewModel.viewState + conflatedStateJob += viewModel.viewState .onEach { renderView(it) } .launchIn(coroutineScope!!) } @@ -92,9 +90,8 @@ class ItrSettingView @JvmOverloads constructor( override fun onDetachedFromWindow() { super.onDetachedFromWindow() findViewTreeLifecycleOwner()?.lifecycle?.removeObserver(viewModel) - coroutineScope?.cancel() job.cancel() - coroutineScope = null + conflatedStateJob.cancel() } private fun renderView(viewState: ViewState) { diff --git a/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/LegacyItrSettingView.kt b/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/LegacyItrSettingView.kt index bb1c9b53b093..f8e2d0638ad6 100644 --- a/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/LegacyItrSettingView.kt +++ b/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/LegacyItrSettingView.kt @@ -16,18 +16,19 @@ package com.duckduckgo.subscriptions.impl.settings.views -import android.annotation.SuppressLint import android.content.Context import android.util.AttributeSet import android.widget.FrameLayout import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.findViewTreeViewModelStoreOwner +import androidx.lifecycle.lifecycleScope import com.duckduckgo.anvil.annotations.InjectWith import com.duckduckgo.common.ui.view.gone import com.duckduckgo.common.ui.view.show import com.duckduckgo.common.ui.viewbinding.viewBinding import com.duckduckgo.common.utils.ConflatedJob +import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.common.utils.ViewViewModelFactory import com.duckduckgo.di.scopes.ViewScope import com.duckduckgo.navigation.api.GlobalActivityStarter @@ -39,9 +40,6 @@ import com.duckduckgo.subscriptions.impl.settings.views.LegacyItrSettingViewMode import com.duckduckgo.subscriptions.impl.ui.SubscriptionsWebViewActivityWithParams import dagger.android.support.AndroidSupportInjection import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -59,7 +57,8 @@ class LegacyItrSettingView @JvmOverloads constructor( @Inject lateinit var globalActivityStarter: GlobalActivityStarter - private var coroutineScope: CoroutineScope? = null + @Inject + lateinit var dispatchers: DispatcherProvider private val binding: LegacyViewItrSettingsBinding by viewBinding() @@ -68,6 +67,7 @@ class LegacyItrSettingView @JvmOverloads constructor( } private var job: ConflatedJob = ConflatedJob() + private var conflatedStateJob: ConflatedJob = ConflatedJob() override fun onAttachedToWindow() { AndroidSupportInjection.inject(this) @@ -79,14 +79,13 @@ class LegacyItrSettingView @JvmOverloads constructor( viewModel.onItr() } - @SuppressLint("NoHardcodedCoroutineDispatcher") - coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) + val coroutineScope = findViewTreeLifecycleOwner()?.lifecycleScope job += viewModel.commands() .onEach { processCommands(it) } .launchIn(coroutineScope!!) - viewModel.viewState + conflatedStateJob += viewModel.viewState .onEach { renderView(it) } .launchIn(coroutineScope!!) } @@ -94,9 +93,8 @@ class LegacyItrSettingView @JvmOverloads constructor( override fun onDetachedFromWindow() { super.onDetachedFromWindow() findViewTreeLifecycleOwner()?.lifecycle?.removeObserver(viewModel) - coroutineScope?.cancel() job.cancel() - coroutineScope = null + conflatedStateJob.cancel() } private fun renderView(viewState: ViewState) { diff --git a/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/LegacyPirSettingView.kt b/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/LegacyPirSettingView.kt index 9acc53b3c04f..674458e26422 100644 --- a/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/LegacyPirSettingView.kt +++ b/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/LegacyPirSettingView.kt @@ -16,18 +16,19 @@ package com.duckduckgo.subscriptions.impl.settings.views -import android.annotation.SuppressLint import android.content.Context import android.util.AttributeSet import android.widget.FrameLayout import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.findViewTreeViewModelStoreOwner +import androidx.lifecycle.lifecycleScope import com.duckduckgo.anvil.annotations.InjectWith import com.duckduckgo.common.ui.view.gone import com.duckduckgo.common.ui.view.show import com.duckduckgo.common.ui.viewbinding.viewBinding import com.duckduckgo.common.utils.ConflatedJob +import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.common.utils.ViewViewModelFactory import com.duckduckgo.di.scopes.ViewScope import com.duckduckgo.navigation.api.GlobalActivityStarter @@ -38,9 +39,6 @@ import com.duckduckgo.subscriptions.impl.settings.views.LegacyPirSettingViewMode import com.duckduckgo.subscriptions.impl.settings.views.LegacyPirSettingViewModel.ViewState import dagger.android.support.AndroidSupportInjection import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -58,7 +56,8 @@ class LegacyPirSettingView @JvmOverloads constructor( @Inject lateinit var globalActivityStarter: GlobalActivityStarter - private var coroutineScope: CoroutineScope? = null + @Inject + lateinit var dispatchers: DispatcherProvider private val binding: LegacyViewPirSettingsBinding by viewBinding() @@ -67,6 +66,7 @@ class LegacyPirSettingView @JvmOverloads constructor( } private var job: ConflatedJob = ConflatedJob() + private var conflatedStateJob: ConflatedJob = ConflatedJob() override fun onAttachedToWindow() { AndroidSupportInjection.inject(this) @@ -78,14 +78,13 @@ class LegacyPirSettingView @JvmOverloads constructor( viewModel.onPir() } - @SuppressLint("NoHardcodedCoroutineDispatcher") - coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) + val coroutineScope = findViewTreeLifecycleOwner()?.lifecycleScope job += viewModel.commands() .onEach { processCommands(it) } .launchIn(coroutineScope!!) - viewModel.viewState + conflatedStateJob += viewModel.viewState .onEach { renderView(it) } .launchIn(coroutineScope!!) } @@ -93,9 +92,8 @@ class LegacyPirSettingView @JvmOverloads constructor( override fun onDetachedFromWindow() { super.onDetachedFromWindow() findViewTreeLifecycleOwner()?.lifecycle?.removeObserver(viewModel) - coroutineScope?.cancel() job.cancel() - coroutineScope = null + conflatedStateJob.cancel() } private fun renderView(viewState: ViewState) { diff --git a/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/LegacyProSettingView.kt b/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/LegacyProSettingView.kt index 2c243a6bd12e..d393218468ce 100644 --- a/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/LegacyProSettingView.kt +++ b/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/LegacyProSettingView.kt @@ -23,6 +23,7 @@ import android.widget.FrameLayout import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.findViewTreeViewModelStoreOwner +import androidx.lifecycle.lifecycleScope import com.duckduckgo.anvil.annotations.InjectWith import com.duckduckgo.common.ui.view.gone import com.duckduckgo.common.ui.view.listitem.CheckListItem.CheckItemStatus.ALERT @@ -30,6 +31,7 @@ import com.duckduckgo.common.ui.view.listitem.CheckListItem.CheckItemStatus.DISA import com.duckduckgo.common.ui.view.show import com.duckduckgo.common.ui.viewbinding.viewBinding import com.duckduckgo.common.utils.ConflatedJob +import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.common.utils.ViewViewModelFactory import com.duckduckgo.di.scopes.ViewScope import com.duckduckgo.navigation.api.GlobalActivityStarter @@ -54,9 +56,6 @@ import com.duckduckgo.subscriptions.impl.ui.SubscriptionSettingsActivity.Compani import com.duckduckgo.subscriptions.impl.ui.SubscriptionsWebViewActivityWithParams import dagger.android.support.AndroidSupportInjection import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -74,7 +73,8 @@ class LegacyProSettingView @JvmOverloads constructor( @Inject lateinit var globalActivityStarter: GlobalActivityStarter - private var coroutineScope: CoroutineScope? = null + @Inject + lateinit var dispatchers: DispatcherProvider private val binding: LegacyViewSettingsBinding by viewBinding() @@ -83,6 +83,7 @@ class LegacyProSettingView @JvmOverloads constructor( } private var job: ConflatedJob = ConflatedJob() + private var conflatedStateJob: ConflatedJob = ConflatedJob() override fun onAttachedToWindow() { AndroidSupportInjection.inject(this) @@ -90,14 +91,13 @@ class LegacyProSettingView @JvmOverloads constructor( findViewTreeLifecycleOwner()?.lifecycle?.addObserver(viewModel) - @SuppressLint("NoHardcodedCoroutineDispatcher") - coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) + val coroutineScope = findViewTreeLifecycleOwner()?.lifecycleScope job += viewModel.commands() .onEach { processCommands(it) } .launchIn(coroutineScope!!) - viewModel.viewState + conflatedStateJob += viewModel.viewState .onEach { renderView(it) } .launchIn(coroutineScope!!) @@ -126,9 +126,8 @@ class LegacyProSettingView @JvmOverloads constructor( override fun onDetachedFromWindow() { super.onDetachedFromWindow() findViewTreeLifecycleOwner()?.lifecycle?.removeObserver(viewModel) - coroutineScope?.cancel() job.cancel() - coroutineScope = null + conflatedStateJob.cancel() } @SuppressLint("ClickableViewAccessibility") diff --git a/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/PirSettingView.kt b/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/PirSettingView.kt index 4d8b37089dfd..8ba2062654d4 100644 --- a/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/PirSettingView.kt +++ b/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/PirSettingView.kt @@ -16,7 +16,6 @@ package com.duckduckgo.subscriptions.impl.settings.views -import android.annotation.SuppressLint import android.content.Context import android.util.AttributeSet import android.widget.FrameLayout @@ -25,9 +24,11 @@ import androidx.core.view.isVisible import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.findViewTreeViewModelStoreOwner +import androidx.lifecycle.lifecycleScope import com.duckduckgo.anvil.annotations.InjectWith import com.duckduckgo.common.ui.viewbinding.viewBinding import com.duckduckgo.common.utils.ConflatedJob +import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.common.utils.ViewViewModelFactory import com.duckduckgo.di.scopes.ViewScope import com.duckduckgo.navigation.api.GlobalActivityStarter @@ -42,9 +43,6 @@ import com.duckduckgo.subscriptions.impl.settings.views.PirSettingViewModel.View import com.duckduckgo.subscriptions.impl.settings.views.PirSettingViewModel.ViewState.PirState.Hidden import dagger.android.support.AndroidSupportInjection import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -62,7 +60,8 @@ class PirSettingView @JvmOverloads constructor( @Inject lateinit var globalActivityStarter: GlobalActivityStarter - private var coroutineScope: CoroutineScope? = null + @Inject + lateinit var dispatchers: DispatcherProvider private val binding: ViewPirSettingsBinding by viewBinding() @@ -71,6 +70,7 @@ class PirSettingView @JvmOverloads constructor( } private var job: ConflatedJob = ConflatedJob() + private val conflatedStateJob = ConflatedJob() override fun onAttachedToWindow() { AndroidSupportInjection.inject(this) @@ -78,24 +78,20 @@ class PirSettingView @JvmOverloads constructor( findViewTreeLifecycleOwner()?.lifecycle?.addObserver(viewModel) - @SuppressLint("NoHardcodedCoroutineDispatcher") - coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) - job += viewModel.commands() .onEach { processCommands(it) } - .launchIn(coroutineScope!!) + .launchIn(findViewTreeLifecycleOwner()?.lifecycleScope!!) - viewModel.viewState + conflatedStateJob += viewModel.viewState .onEach { renderView(it) } - .launchIn(coroutineScope!!) + .launchIn(findViewTreeLifecycleOwner()?.lifecycleScope!!) } override fun onDetachedFromWindow() { super.onDetachedFromWindow() findViewTreeLifecycleOwner()?.lifecycle?.removeObserver(viewModel) - coroutineScope?.cancel() job.cancel() - coroutineScope = null + conflatedStateJob.cancel() } private fun renderView(viewState: ViewState) { diff --git a/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/ProSettingView.kt b/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/ProSettingView.kt index 0046709ced79..ffb76e495497 100644 --- a/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/ProSettingView.kt +++ b/subscriptions/subscriptions-impl/src/main/java/com/duckduckgo/subscriptions/impl/settings/views/ProSettingView.kt @@ -25,9 +25,11 @@ import androidx.core.view.isVisible import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.findViewTreeViewModelStoreOwner +import androidx.lifecycle.lifecycleScope import com.duckduckgo.anvil.annotations.InjectWith import com.duckduckgo.common.ui.viewbinding.viewBinding import com.duckduckgo.common.utils.ConflatedJob +import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.common.utils.ViewViewModelFactory import com.duckduckgo.di.scopes.ViewScope import com.duckduckgo.mobile.android.R as CommonR @@ -53,9 +55,6 @@ import com.duckduckgo.subscriptions.impl.ui.SubscriptionSettingsActivity.Compani import com.duckduckgo.subscriptions.impl.ui.SubscriptionsWebViewActivityWithParams import dagger.android.support.AndroidSupportInjection import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -73,7 +72,8 @@ class ProSettingView @JvmOverloads constructor( @Inject lateinit var globalActivityStarter: GlobalActivityStarter - private var coroutineScope: CoroutineScope? = null + @Inject + lateinit var dispatchers: DispatcherProvider private val binding: ViewSettingsBinding by viewBinding() @@ -82,6 +82,7 @@ class ProSettingView @JvmOverloads constructor( } private var job: ConflatedJob = ConflatedJob() + private var conflatedStateJob: ConflatedJob = ConflatedJob() override fun onAttachedToWindow() { AndroidSupportInjection.inject(this) @@ -89,14 +90,13 @@ class ProSettingView @JvmOverloads constructor( findViewTreeLifecycleOwner()?.lifecycle?.addObserver(viewModel) - @SuppressLint("NoHardcodedCoroutineDispatcher") - coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) + val coroutineScope = findViewTreeLifecycleOwner()?.lifecycleScope job += viewModel.commands() .onEach { processCommands(it) } .launchIn(coroutineScope!!) - viewModel.viewState + conflatedStateJob += viewModel.viewState .onEach { renderView(it) } .launchIn(coroutineScope!!) @@ -125,9 +125,8 @@ class ProSettingView @JvmOverloads constructor( override fun onDetachedFromWindow() { super.onDetachedFromWindow() findViewTreeLifecycleOwner()?.lifecycle?.removeObserver(viewModel) - coroutineScope?.cancel() job.cancel() - coroutineScope = null + conflatedStateJob.cancel() } @SuppressLint("ClickableViewAccessibility") diff --git a/sync/sync-impl/src/main/java/com/duckduckgo/sync/impl/promotion/bookmarks/SyncBookmarksPromotion.kt b/sync/sync-impl/src/main/java/com/duckduckgo/sync/impl/promotion/bookmarks/SyncBookmarksPromotion.kt index f4b2f2ec6979..c751b70d0079 100644 --- a/sync/sync-impl/src/main/java/com/duckduckgo/sync/impl/promotion/bookmarks/SyncBookmarksPromotion.kt +++ b/sync/sync-impl/src/main/java/com/duckduckgo/sync/impl/promotion/bookmarks/SyncBookmarksPromotion.kt @@ -21,7 +21,9 @@ import android.util.AttributeSet import android.view.View import android.widget.FrameLayout import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.findViewTreeViewModelStoreOwner +import androidx.lifecycle.lifecycleScope import com.duckduckgo.anvil.annotations.InjectWith import com.duckduckgo.common.ui.view.MessageCta.Message import com.duckduckgo.common.ui.view.show @@ -44,8 +46,6 @@ import com.duckduckgo.sync.impl.ui.SyncActivityWithSourceParams import com.squareup.anvil.annotations.ContributesMultibinding import dagger.android.support.AndroidSupportInjection import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -87,16 +87,14 @@ class SyncBookmarksPromotionView @JvmOverloads constructor( } private var job: ConflatedJob = ConflatedJob() - private lateinit var coroutineScope: CoroutineScope override fun onAttachedToWindow() { AndroidSupportInjection.inject(this) - coroutineScope = CoroutineScope(SupervisorJob() + dispatchers.main()) super.onAttachedToWindow() job += viewModel.commands() .onEach { processCommand(it) } - .launchIn(coroutineScope) + .launchIn(findViewTreeLifecycleOwner()?.lifecycleScope!!) configureMessage() @@ -105,7 +103,6 @@ class SyncBookmarksPromotionView @JvmOverloads constructor( override fun onDetachedFromWindow() { super.onDetachedFromWindow() - coroutineScope.cancel() job.cancel() } diff --git a/sync/sync-impl/src/main/java/com/duckduckgo/sync/impl/promotion/passwords/SyncPasswordsPromotion.kt b/sync/sync-impl/src/main/java/com/duckduckgo/sync/impl/promotion/passwords/SyncPasswordsPromotion.kt index a2d357f4f029..63611e6761c2 100644 --- a/sync/sync-impl/src/main/java/com/duckduckgo/sync/impl/promotion/passwords/SyncPasswordsPromotion.kt +++ b/sync/sync-impl/src/main/java/com/duckduckgo/sync/impl/promotion/passwords/SyncPasswordsPromotion.kt @@ -21,7 +21,9 @@ import android.util.AttributeSet import android.view.View import android.widget.FrameLayout import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.findViewTreeViewModelStoreOwner +import androidx.lifecycle.lifecycleScope import com.duckduckgo.anvil.annotations.InjectWith import com.duckduckgo.anvil.annotations.PriorityKey import com.duckduckgo.autofill.api.promotion.PasswordsScreenPromotionPlugin @@ -46,8 +48,6 @@ import com.duckduckgo.sync.impl.ui.SyncActivityWithSourceParams import com.squareup.anvil.annotations.ContributesMultibinding import dagger.android.support.AndroidSupportInjection import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -90,16 +90,14 @@ class SyncPasswordsPromotionView @JvmOverloads constructor( } private var job: ConflatedJob = ConflatedJob() - private lateinit var coroutineScope: CoroutineScope override fun onAttachedToWindow() { AndroidSupportInjection.inject(this) - coroutineScope = CoroutineScope(SupervisorJob() + dispatchers.main()) super.onAttachedToWindow() job += viewModel.commands() .onEach { processCommand(it) } - .launchIn(coroutineScope) + .launchIn(findViewTreeLifecycleOwner()?.lifecycleScope!!) configureMessage() @@ -108,7 +106,6 @@ class SyncPasswordsPromotionView @JvmOverloads constructor( override fun onDetachedFromWindow() { super.onDetachedFromWindow() - coroutineScope.cancel() job.cancel() } diff --git a/sync/sync-impl/src/main/java/com/duckduckgo/sync/impl/ui/SyncDisabledView.kt b/sync/sync-impl/src/main/java/com/duckduckgo/sync/impl/ui/SyncDisabledView.kt index e02e5768006a..22227260930a 100644 --- a/sync/sync-impl/src/main/java/com/duckduckgo/sync/impl/ui/SyncDisabledView.kt +++ b/sync/sync-impl/src/main/java/com/duckduckgo/sync/impl/ui/SyncDisabledView.kt @@ -16,7 +16,6 @@ package com.duckduckgo.sync.impl.ui -import android.annotation.SuppressLint import android.content.Context import android.util.AttributeSet import android.widget.FrameLayout @@ -24,16 +23,16 @@ import androidx.core.view.isVisible import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.findViewTreeViewModelStoreOwner +import androidx.lifecycle.lifecycleScope import com.duckduckgo.anvil.annotations.InjectWith import com.duckduckgo.common.ui.viewbinding.viewBinding +import com.duckduckgo.common.utils.ConflatedJob +import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.di.scopes.ViewScope import com.duckduckgo.sync.impl.databinding.ViewSyncDisabledWarningBinding import com.duckduckgo.sync.impl.ui.SyncDisabledViewModel.ViewState import dagger.android.support.AndroidSupportInjection import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -48,7 +47,8 @@ class SyncDisabledView @JvmOverloads constructor( @Inject lateinit var viewModelFactory: SyncDisabledViewModel.Factory - private var coroutineScope: CoroutineScope? = null + @Inject + lateinit var dispatchers: DispatcherProvider private val binding: ViewSyncDisabledWarningBinding by viewBinding() @@ -56,25 +56,23 @@ class SyncDisabledView @JvmOverloads constructor( ViewModelProvider(findViewTreeViewModelStoreOwner()!!, viewModelFactory)[SyncDisabledViewModel::class.java] } + private val conflatedJob = ConflatedJob() + override fun onAttachedToWindow() { AndroidSupportInjection.inject(this) super.onAttachedToWindow() findViewTreeLifecycleOwner()?.lifecycle?.addObserver(viewModel) - @SuppressLint("NoHardcodedCoroutineDispatcher") - coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) - - viewModel.viewState() + conflatedJob += viewModel.viewState() .onEach { render(it) } - .launchIn(coroutineScope!!) + .launchIn(findViewTreeLifecycleOwner()?.lifecycleScope!!) } override fun onDetachedFromWindow() { super.onDetachedFromWindow() findViewTreeLifecycleOwner()?.lifecycle?.removeObserver(viewModel) - coroutineScope?.cancel() - coroutineScope = null + conflatedJob.cancel() } private fun render(viewState: ViewState) { diff --git a/sync/sync-impl/src/main/java/com/duckduckgo/sync/impl/ui/SyncErrorView.kt b/sync/sync-impl/src/main/java/com/duckduckgo/sync/impl/ui/SyncErrorView.kt index 0f26737b1342..018cf203c7cc 100644 --- a/sync/sync-impl/src/main/java/com/duckduckgo/sync/impl/ui/SyncErrorView.kt +++ b/sync/sync-impl/src/main/java/com/duckduckgo/sync/impl/ui/SyncErrorView.kt @@ -16,7 +16,6 @@ package com.duckduckgo.sync.impl.ui -import android.annotation.SuppressLint import android.content.Context import android.util.AttributeSet import android.widget.FrameLayout @@ -24,17 +23,17 @@ import androidx.core.view.isVisible import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.findViewTreeViewModelStoreOwner +import androidx.lifecycle.lifecycleScope import com.duckduckgo.anvil.annotations.InjectWith import com.duckduckgo.common.ui.viewbinding.viewBinding +import com.duckduckgo.common.utils.ConflatedJob +import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.common.utils.ViewViewModelFactory import com.duckduckgo.di.scopes.ViewScope import com.duckduckgo.sync.impl.databinding.ViewSyncErrorWarningBinding import com.duckduckgo.sync.impl.ui.SyncErrorViewModel.ViewState import dagger.android.support.AndroidSupportInjection import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -49,7 +48,8 @@ class SyncErrorView @JvmOverloads constructor( @Inject lateinit var viewModelFactory: ViewViewModelFactory - private var coroutineScope: CoroutineScope? = null + @Inject + lateinit var dispatchers: DispatcherProvider private val binding: ViewSyncErrorWarningBinding by viewBinding() @@ -57,25 +57,23 @@ class SyncErrorView @JvmOverloads constructor( ViewModelProvider(findViewTreeViewModelStoreOwner()!!, viewModelFactory)[SyncErrorViewModel::class.java] } + private val conflatedJob = ConflatedJob() + override fun onAttachedToWindow() { AndroidSupportInjection.inject(this) super.onAttachedToWindow() findViewTreeLifecycleOwner()?.lifecycle?.addObserver(viewModel) - @SuppressLint("NoHardcodedCoroutineDispatcher") - coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) - - viewModel.viewState() + conflatedJob += viewModel.viewState() .onEach { render(it) } - .launchIn(coroutineScope!!) + .launchIn(findViewTreeLifecycleOwner()?.lifecycleScope!!) } override fun onDetachedFromWindow() { super.onDetachedFromWindow() findViewTreeLifecycleOwner()?.lifecycle?.removeObserver(viewModel) - coroutineScope?.cancel() - coroutineScope = null + conflatedJob.cancel() } private fun render(viewState: ViewState) { diff --git a/sync/sync-impl/src/main/java/com/duckduckgo/sync/impl/ui/qrcode/SyncBarcodeView.kt b/sync/sync-impl/src/main/java/com/duckduckgo/sync/impl/ui/qrcode/SyncBarcodeView.kt index 0b5aeb941591..d4b6994c0dec 100644 --- a/sync/sync-impl/src/main/java/com/duckduckgo/sync/impl/ui/qrcode/SyncBarcodeView.kt +++ b/sync/sync-impl/src/main/java/com/duckduckgo/sync/impl/ui/qrcode/SyncBarcodeView.kt @@ -33,8 +33,11 @@ import androidx.core.content.ContextCompat import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.findViewTreeViewModelStoreOwner +import androidx.lifecycle.lifecycleScope import com.duckduckgo.anvil.annotations.InjectWith import com.duckduckgo.common.ui.viewbinding.viewBinding +import com.duckduckgo.common.utils.ConflatedJob +import com.duckduckgo.common.utils.DispatcherProvider import com.duckduckgo.di.scopes.ViewScope import com.duckduckgo.sync.impl.R import com.duckduckgo.sync.impl.databinding.ViewSquareDecoratedBarcodeBinding @@ -46,9 +49,7 @@ import com.duckduckgo.sync.impl.ui.qrcode.SquareDecoratedBarcodeViewModel.Comman import com.duckduckgo.sync.impl.ui.qrcode.SquareDecoratedBarcodeViewModel.ViewState import dagger.android.support.AndroidSupportInjection import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -68,7 +69,8 @@ constructor( @Inject lateinit var viewModelFactory: SquareDecoratedBarcodeViewModel.Factory - private lateinit var coroutineScope: CoroutineScope + @Inject + lateinit var dispatchers: DispatcherProvider private val cameraBlockedDrawable by lazy { ContextCompat.getDrawable(context, R.drawable.camera_blocked) @@ -84,28 +86,36 @@ constructor( ViewModelProvider(findViewTreeViewModelStoreOwner()!!, viewModelFactory)[SquareDecoratedBarcodeViewModel::class.java] } + private val conflatedStateJob = ConflatedJob() + private val conflatedCommandJob = ConflatedJob() + override fun onAttachedToWindow() { AndroidSupportInjection.inject(this) super.onAttachedToWindow() findViewTreeLifecycleOwner()?.lifecycle?.addObserver(viewModel) - @SuppressLint("NoHardcodedCoroutineDispatcher") - coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main) + val scope = findViewTreeLifecycleOwner()?.lifecycleScope!! - viewModel.viewState + conflatedStateJob += viewModel.viewState .onEach { render(it) } - .launchIn(coroutineScope) + .launchIn(scope) - viewModel.commands() + conflatedCommandJob += viewModel.commands() .onEach { processCommands(it) } - .launchIn(coroutineScope) + .launchIn(scope) binding.goToSettingsButton.setOnClickListener { viewModel.goToSettings() } } + override fun onDetachedFromWindow() { + conflatedStateJob.cancel() + conflatedCommandJob.cancel() + super.onDetachedFromWindow() + } + fun resume() { binding.barcodeView.resume() }