From e052e488d3a323b64e1a7807c7d501082df4c408 Mon Sep 17 00:00:00 2001 From: Ahmad Kazimi Date: Mon, 25 Apr 2022 01:20:36 +0400 Subject: [PATCH] I followed rule of 3. and apply it on lifecycleScope.launch {repeatOnLifecycle(Lifecycle.State.STARTED) {}} I found many usage in same level and created ext. fun to handle flow collect operation. --- depconstraints/build.gradle.kts | 2 +- .../apps/iosched/ui/LauncherActivity.kt | 24 ++++------ .../samples/apps/iosched/ui/MainActivity.kt | 46 ++++++------------- .../map/MapVariantSelectionDialogFragment.kt | 9 ++-- .../ui/onboarding/OnboardingActivity.kt | 16 ++----- .../RemoveReservationDialogFragment.kt | 19 ++++---- .../ui/settings/ThemeSettingDialogFragment.kt | 31 ++++--------- .../iosched/ui/signin/SignInDialogFragment.kt | 20 ++++---- .../ui/signin/SignOutDialogFragment.kt | 13 ++---- .../samples/apps/iosched/util/Extensions.kt | 31 ++++++++++--- 10 files changed, 86 insertions(+), 125 deletions(-) diff --git a/depconstraints/build.gradle.kts b/depconstraints/build.gradle.kts index 93c4e1de45..4e087fba22 100644 --- a/depconstraints/build.gradle.kts +++ b/depconstraints/build.gradle.kts @@ -55,7 +55,7 @@ val hamcrest = "1.3" val hilt = Versions.HILT_AGP val junit = "4.13" val junitExt = "1.1.1" -val lifecycle = "2.4.0-alpha01" +val lifecycle = "2.4.1" val lottie = "3.0.0" val material = "1.4.0-beta01" val mockito = "3.3.1" diff --git a/mobile/src/main/java/com/google/samples/apps/iosched/ui/LauncherActivity.kt b/mobile/src/main/java/com/google/samples/apps/iosched/ui/LauncherActivity.kt index fb6cc41349..6d96548f7f 100644 --- a/mobile/src/main/java/com/google/samples/apps/iosched/ui/LauncherActivity.kt +++ b/mobile/src/main/java/com/google/samples/apps/iosched/ui/LauncherActivity.kt @@ -26,8 +26,8 @@ import androidx.lifecycle.repeatOnLifecycle import com.google.samples.apps.iosched.ui.LaunchNavigatonAction.NavigateToMainActivityAction import com.google.samples.apps.iosched.ui.LaunchNavigatonAction.NavigateToOnboardingAction import com.google.samples.apps.iosched.ui.onboarding.OnboardingActivity +import com.google.samples.apps.iosched.util.collectLifecycleFlow import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch /** @@ -41,20 +41,16 @@ class LauncherActivity : AppCompatActivity() { val viewModel: LaunchViewModel by viewModels() - lifecycleScope.launch { - lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.launchDestination.collect { action -> - when (action) { - is NavigateToMainActivityAction -> startActivity( - Intent(this@LauncherActivity, MainActivity::class.java) - ) - is NavigateToOnboardingAction -> startActivity( - Intent(this@LauncherActivity, OnboardingActivity::class.java) - ) - } - finish() - } + collectLifecycleFlow(viewModel.launchDestination) { action -> + when (action) { + is NavigateToMainActivityAction -> startActivity( + Intent(this@LauncherActivity, MainActivity::class.java) + ) + is NavigateToOnboardingAction -> startActivity( + Intent(this@LauncherActivity, OnboardingActivity::class.java) + ) } + finish() } } } diff --git a/mobile/src/main/java/com/google/samples/apps/iosched/ui/MainActivity.kt b/mobile/src/main/java/com/google/samples/apps/iosched/ui/MainActivity.kt index f7f01a0e50..082aabfd68 100644 --- a/mobile/src/main/java/com/google/samples/apps/iosched/ui/MainActivity.kt +++ b/mobile/src/main/java/com/google/samples/apps/iosched/ui/MainActivity.kt @@ -30,9 +30,6 @@ import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.isVisible import androidx.core.view.updatePadding -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.NavController import androidx.navigation.fragment.NavHostFragment import androidx.navigation.ui.AppBarConfiguration @@ -52,11 +49,10 @@ import com.google.samples.apps.iosched.ui.messages.SnackbarMessageManager import com.google.samples.apps.iosched.ui.signin.SignInDialogFragment import com.google.samples.apps.iosched.ui.signin.SignOutDialogFragment import com.google.samples.apps.iosched.util.HeightTopWindowInsetsListener +import com.google.samples.apps.iosched.util.collectLifecycleFlow import com.google.samples.apps.iosched.util.signin.FirebaseAuthErrorCodeConverter import com.google.samples.apps.iosched.util.updateForTheme import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.launch import timber.log.Timber import java.util.UUID import javax.inject.Inject @@ -155,36 +151,20 @@ class MainActivity : AppCompatActivity(), NavigationHost { navigateTo(requestedNavId) } - lifecycleScope.launch { - lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { - launch { - viewModel.navigationActions.collect { action -> - when (action) { - MainNavigationAction.OpenSignIn -> openSignInDialog() - MainNavigationAction.OpenSignOut -> openSignOutDialog() - } - } - } - launch { - viewModel.theme.collect { theme -> - updateForTheme(theme) - } - } - // AR-related Flows - launch { - viewModel.arCoreAvailability.collect { result -> - // Do nothing - activate flow - Timber.d("ArCoreAvailability = $result") - } - } - launch { - viewModel.pinnedSessionsJson.collect { /* Do nothing - activate flow */ } - } - launch { - viewModel.canSignedInUserDemoAr.collect { /* Do nothing - activate flow */ } - } + collectLifecycleFlow(viewModel.navigationActions) { action -> + when (action) { + MainNavigationAction.OpenSignIn -> openSignInDialog() + MainNavigationAction.OpenSignOut -> openSignOutDialog() } } + collectLifecycleFlow(viewModel.navigationActions) { /* Do nothing - activate flow */} + collectLifecycleFlow(viewModel.theme) { /* Do nothing - activate flow */ } + collectLifecycleFlow(viewModel.arCoreAvailability) { result -> + // Do nothing - activate flow + Timber.d("ArCoreAvailability = $result") + } + collectLifecycleFlow(viewModel.pinnedSessionsJson) { /* Do nothing - activate flow */ } + collectLifecycleFlow(viewModel.canSignedInUserDemoAr) { /* Do nothing - activate flow */ } binding.navigationRail?.let { ViewCompat.setOnApplyWindowInsetsListener(it) { view, insets -> diff --git a/mobile/src/main/java/com/google/samples/apps/iosched/ui/map/MapVariantSelectionDialogFragment.kt b/mobile/src/main/java/com/google/samples/apps/iosched/ui/map/MapVariantSelectionDialogFragment.kt index c0e4069d6f..546e73c536 100644 --- a/mobile/src/main/java/com/google/samples/apps/iosched/ui/map/MapVariantSelectionDialogFragment.kt +++ b/mobile/src/main/java/com/google/samples/apps/iosched/ui/map/MapVariantSelectionDialogFragment.kt @@ -32,6 +32,7 @@ import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import androidx.recyclerview.widget.RecyclerView import com.google.samples.apps.iosched.R +import com.google.samples.apps.iosched.util.collectLifecycleFlow import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch @@ -61,12 +62,8 @@ class MapVariantSelectionDialogFragment : AppCompatDialogFragment() { adapter = MapVariantAdapter(::selectMapVariant) view.findViewById(R.id.map_variant_list).adapter = adapter - lifecycleScope.launch { - lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { - mapViewModel.mapVariant.collect { - adapter.currentSelection = it - } - } + collectLifecycleFlow(mapViewModel.mapVariant) { + adapter.currentSelection = it } } diff --git a/mobile/src/main/java/com/google/samples/apps/iosched/ui/onboarding/OnboardingActivity.kt b/mobile/src/main/java/com/google/samples/apps/iosched/ui/onboarding/OnboardingActivity.kt index 63ddc5057a..ca542713e7 100644 --- a/mobile/src/main/java/com/google/samples/apps/iosched/ui/onboarding/OnboardingActivity.kt +++ b/mobile/src/main/java/com/google/samples/apps/iosched/ui/onboarding/OnboardingActivity.kt @@ -23,16 +23,12 @@ import androidx.appcompat.app.AppCompatActivity import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.updatePadding -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle import com.google.samples.apps.iosched.R import com.google.samples.apps.iosched.shared.util.inTransaction import com.google.samples.apps.iosched.ui.signin.SignInDialogFragment +import com.google.samples.apps.iosched.util.collectLifecycleFlow import com.google.samples.apps.iosched.util.doOnApplyWindowInsets import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.launch @AndroidEntryPoint class OnboardingActivity : AppCompatActivity() { @@ -57,13 +53,9 @@ class OnboardingActivity : AppCompatActivity() { } } - lifecycleScope.launch { - lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.navigationActions.collect { action -> - if (action == OnboardingNavigationAction.NavigateToSignInDialog) { - openSignInDialog() - } - } + collectLifecycleFlow(viewModel.navigationActions) { action -> + if (action == OnboardingNavigationAction.NavigateToSignInDialog) { + openSignInDialog() } } } diff --git a/mobile/src/main/java/com/google/samples/apps/iosched/ui/reservation/RemoveReservationDialogFragment.kt b/mobile/src/main/java/com/google/samples/apps/iosched/ui/reservation/RemoveReservationDialogFragment.kt index 06edc2db8b..f4b8113240 100644 --- a/mobile/src/main/java/com/google/samples/apps/iosched/ui/reservation/RemoveReservationDialogFragment.kt +++ b/mobile/src/main/java/com/google/samples/apps/iosched/ui/reservation/RemoveReservationDialogFragment.kt @@ -31,6 +31,7 @@ import androidx.lifecycle.repeatOnLifecycle import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.samples.apps.iosched.R import com.google.samples.apps.iosched.model.SessionId +import com.google.samples.apps.iosched.util.collectLifecycleFlow import com.google.samples.apps.iosched.util.makeBold import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.flow.collect @@ -92,17 +93,13 @@ class RemoveReservationDialogFragment : AppCompatDialogFragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - lifecycleScope.launch { - lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.snackbarMessages.collect { - // Using Toast instead of Snackbar as it's easier for DialogFragment - Toast.makeText( - requireContext(), - it.messageId, - if (it.longDuration) Toast.LENGTH_LONG else Toast.LENGTH_SHORT - ).show() - } - } + collectLifecycleFlow(viewModel.snackbarMessages) { + // Using Toast instead of Snackbar as it's easier for DialogFragment + Toast.makeText( + requireContext(), + it.messageId, + if (it.longDuration) Toast.LENGTH_LONG else Toast.LENGTH_SHORT + ).show() } } diff --git a/mobile/src/main/java/com/google/samples/apps/iosched/ui/settings/ThemeSettingDialogFragment.kt b/mobile/src/main/java/com/google/samples/apps/iosched/ui/settings/ThemeSettingDialogFragment.kt index c00439a7a4..3efa9bc685 100644 --- a/mobile/src/main/java/com/google/samples/apps/iosched/ui/settings/ThemeSettingDialogFragment.kt +++ b/mobile/src/main/java/com/google/samples/apps/iosched/ui/settings/ThemeSettingDialogFragment.kt @@ -22,15 +22,11 @@ import android.widget.ArrayAdapter import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatDialogFragment import androidx.fragment.app.viewModels -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.samples.apps.iosched.R import com.google.samples.apps.iosched.model.Theme +import com.google.samples.apps.iosched.util.collectLifecycleFlow import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.launch @AndroidEntryPoint class ThemeSettingDialogFragment : AppCompatDialogFragment() { @@ -61,26 +57,17 @@ class ThemeSettingDialogFragment : AppCompatDialogFragment() { super.onCreate(savedInstanceState) // Note you don't need to use viewLifecycleOwner in DialogFragment. - lifecycleScope.launch { - lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.availableThemes.collect { themes -> - listAdapter.clear() - listAdapter.addAll( - themes.map { theme -> - ThemeHolder(theme, getTitleForTheme(theme)) - } - ) - - updateSelectedItem(viewModel.theme.value) + collectLifecycleFlow(viewModel.availableThemes) { themes -> + listAdapter.clear() + listAdapter.addAll( + themes.map { theme -> + ThemeHolder(theme, getTitleForTheme(theme)) } - } + ) + updateSelectedItem(viewModel.theme.value) } - lifecycleScope.launch { - lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.theme.collect { updateSelectedItem(it) } - } - } + collectLifecycleFlow(viewModel.theme) { updateSelectedItem(it) } } private fun updateSelectedItem(selected: Theme?) { diff --git a/mobile/src/main/java/com/google/samples/apps/iosched/ui/signin/SignInDialogFragment.kt b/mobile/src/main/java/com/google/samples/apps/iosched/ui/signin/SignInDialogFragment.kt index 3c0809641d..467df72d72 100644 --- a/mobile/src/main/java/com/google/samples/apps/iosched/ui/signin/SignInDialogFragment.kt +++ b/mobile/src/main/java/com/google/samples/apps/iosched/ui/signin/SignInDialogFragment.kt @@ -30,6 +30,7 @@ import androidx.lifecycle.repeatOnLifecycle import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.samples.apps.iosched.databinding.DialogSignInBinding import com.google.samples.apps.iosched.ui.signin.SignInNavigationAction.RequestSignIn +import com.google.samples.apps.iosched.util.collectLifecycleFlow import com.google.samples.apps.iosched.util.executeAfter import com.google.samples.apps.iosched.util.signin.SignInHandler import dagger.hilt.android.AndroidEntryPoint @@ -69,17 +70,14 @@ class SignInDialogFragment : AppCompatDialogFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - lifecycleScope.launch { - lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { - signInViewModel.signInNavigationActions.collect { action -> - if (action == RequestSignIn) { - activity?.startActivityForResult( - signInHandler.makeSignInIntent(), - REQUEST_CODE_SIGN_IN - ) - dismiss() - } - } + collectLifecycleFlow(signInViewModel.signInNavigationActions) { + action -> + if (action == RequestSignIn) { + activity?.startActivityForResult( + signInHandler.makeSignInIntent(), + REQUEST_CODE_SIGN_IN + ) + dismiss() } } diff --git a/mobile/src/main/java/com/google/samples/apps/iosched/ui/signin/SignOutDialogFragment.kt b/mobile/src/main/java/com/google/samples/apps/iosched/ui/signin/SignOutDialogFragment.kt index 97e82f1a8f..ce99c6a0fd 100644 --- a/mobile/src/main/java/com/google/samples/apps/iosched/ui/signin/SignOutDialogFragment.kt +++ b/mobile/src/main/java/com/google/samples/apps/iosched/ui/signin/SignOutDialogFragment.kt @@ -34,6 +34,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.samples.apps.iosched.databinding.DialogSignOutBinding import com.google.samples.apps.iosched.shared.data.signin.AuthenticatedUserInfo import com.google.samples.apps.iosched.ui.signin.SignInNavigationAction.RequestSignOut +import com.google.samples.apps.iosched.util.collectLifecycleFlow import com.google.samples.apps.iosched.util.executeAfter import com.google.samples.apps.iosched.util.signin.SignInHandler import dagger.hilt.android.AndroidEntryPoint @@ -73,14 +74,10 @@ class SignOutDialogFragment : AppCompatDialogFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - lifecycleScope.launch { - lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { - signInViewModel.signInNavigationActions.collect { action -> - if (action == RequestSignOut) { - signInHandler.signOut(requireContext()) - dismiss() - } - } + collectLifecycleFlow(signInViewModel.signInNavigationActions) { action -> + if (action == RequestSignOut) { + signInHandler.signOut(requireContext()) + dismiss() } } diff --git a/mobile/src/main/java/com/google/samples/apps/iosched/util/Extensions.kt b/mobile/src/main/java/com/google/samples/apps/iosched/util/Extensions.kt index 26ab7e07c3..ca4092c8f5 100644 --- a/mobile/src/main/java/com/google/samples/apps/iosched/util/Extensions.kt +++ b/mobile/src/main/java/com/google/samples/apps/iosched/util/Extensions.kt @@ -23,12 +23,8 @@ import android.content.res.TypedArray import android.graphics.Typeface import android.net.wifi.WifiConfiguration import android.os.Build +import android.text.* import android.text.Layout.Alignment -import android.text.Spannable -import android.text.SpannableString -import android.text.Spanned -import android.text.StaticLayout -import android.text.TextPaint import android.text.style.StyleSpan import android.util.TypedValue import android.view.View @@ -37,6 +33,7 @@ import androidx.annotation.ColorInt import androidx.annotation.DimenRes import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatDelegate +import androidx.appcompat.app.AppCompatDialogFragment import androidx.core.content.res.ResourcesCompat import androidx.core.os.BuildCompat import androidx.core.view.ViewCompat @@ -44,9 +41,13 @@ import androidx.core.view.WindowInsetsCompat import androidx.databinding.ObservableBoolean import androidx.databinding.ViewDataBinding import androidx.drawerlayout.widget.DrawerLayout -import androidx.lifecycle.LiveData -import androidx.lifecycle.MediatorLiveData +import androidx.fragment.app.Fragment +import androidx.lifecycle.* import com.google.samples.apps.iosched.model.Theme +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.FlowCollector +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.launch fun ObservableBoolean.hasSameValue(other: ObservableBoolean) = get() == other.get() @@ -326,3 +327,19 @@ fun Context.getColorFromTheme(colorAttributeId: Int): Int { typedArray.recycle() return color } +/** to collect flow in a life cycle scope for [AppCompatActivity] * */ +fun AppCompatActivity.collectLifecycleFlow(flow: Flow, collect: FlowCollector) { + lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.STARTED) { + flow.collect(collect) + } + } +} +/** to collect flow in a life cycle scope for [Fragment] * */ +fun Fragment.collectLifecycleFlow(flow: Flow, collect: FlowCollector) { + lifecycleScope.launch { + lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { + flow.collect(collect) + } + } +}