Skip to content
This repository was archived by the owner on Jan 5, 2023. It is now read-only.

Commit 36f30ad

Browse files
committed
Consolidate session star click handling
Consolidates all session star click handling into one class that can be injected into all ViewModels that need it. This also decouples session click handling from session star click handling. Change-Id: I780157b1df0916d9db9b6216c8836e3c96689c62
1 parent 6f2cd9d commit 36f30ad

26 files changed

+288
-142
lines changed

mobile/src/main/java/com/google/samples/apps/iosched/ui/feed/FeedViewModel.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ import com.google.samples.apps.iosched.shared.util.toEpochMilli
4747
import com.google.samples.apps.iosched.shared.util.tryOffer
4848
import com.google.samples.apps.iosched.ui.messages.SnackbarMessage
4949
import com.google.samples.apps.iosched.ui.messages.SnackbarMessageManager
50-
import com.google.samples.apps.iosched.ui.sessioncommon.EventActions
50+
import com.google.samples.apps.iosched.ui.sessioncommon.OnSessionClickListener
51+
import com.google.samples.apps.iosched.ui.sessioncommon.OnSessionStarClickListener
5152
import com.google.samples.apps.iosched.ui.signin.SignInViewModelDelegate
5253
import com.google.samples.apps.iosched.ui.theme.ThemedActivityDelegate
5354
import com.google.samples.apps.iosched.util.WhileViewSubscribed
@@ -315,7 +316,7 @@ class FeedViewModel @Inject constructor(
315316
}
316317
}
317318

318-
interface FeedEventListener : EventActions {
319+
interface FeedEventListener : OnSessionClickListener, OnSessionStarClickListener {
319320
fun openSchedule(showOnlyPinnedSessions: Boolean)
320321
fun signIn()
321322
fun openMap(moment: Moment)

mobile/src/main/java/com/google/samples/apps/iosched/ui/schedule/ScheduleFragment.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,11 +158,12 @@ class ScheduleFragment : Fragment() {
158158

159159
// Session list configuration
160160
sessionsAdapter = SessionsAdapter(
161-
scheduleTwoPaneViewModel,
162161
tagViewPool,
163162
scheduleViewModel.showReservations,
164163
scheduleViewModel.timeZoneId,
165-
viewLifecycleOwner
164+
viewLifecycleOwner,
165+
scheduleTwoPaneViewModel, // OnSessionClickListener
166+
scheduleTwoPaneViewModel // OnSessionStarClickListener
166167
)
167168
scheduleRecyclerView.apply {
168169
adapter = sessionsAdapter

mobile/src/main/java/com/google/samples/apps/iosched/ui/schedule/ScheduleItemBindingAdapter.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,12 @@ import org.threeten.bp.ZonedDateTime
3838
)
3939
fun sessionDateTimeLocation(
4040
textView: TextView,
41-
startTime: ZonedDateTime,
41+
startTime: ZonedDateTime?,
4242
zoneId: ZoneId?,
4343
showTime: Boolean,
4444
room: Room?
4545
) {
46+
startTime ?: return
4647
zoneId ?: return
4748
val roomName = room?.name ?: "-"
4849
val localStartTime = TimeUtils.zonedTime(startTime, zoneId)

mobile/src/main/java/com/google/samples/apps/iosched/ui/schedule/ScheduleTwoPaneFragment.kt

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -23,21 +23,21 @@ import android.view.ViewGroup
2323
import androidx.activity.OnBackPressedCallback
2424
import androidx.core.view.doOnNextLayout
2525
import androidx.fragment.app.activityViewModels
26-
import androidx.lifecycle.lifecycleScope
2726
import androidx.navigation.NavController
2827
import androidx.navigation.NavDestination
2928
import androidx.navigation.fragment.NavHostFragment
3029
import androidx.slidingpanelayout.widget.SlidingPaneLayout
3130
import com.google.samples.apps.iosched.R
3231
import com.google.samples.apps.iosched.ScheduleDetailNavGraphDirections
3332
import com.google.samples.apps.iosched.databinding.FragmentScheduleTwoPaneBinding
34-
import com.google.samples.apps.iosched.shared.result.EventObserver
3533
import com.google.samples.apps.iosched.ui.MainNavigationFragment
3634
import com.google.samples.apps.iosched.ui.messages.SnackbarMessageManager
3735
import com.google.samples.apps.iosched.ui.messages.setupSnackbarManager
3836
import com.google.samples.apps.iosched.ui.signin.SignInDialogFragment
37+
import com.google.samples.apps.iosched.util.launchAndRepeatWithViewLifecycle
3938
import dagger.hilt.android.AndroidEntryPoint
4039
import kotlinx.coroutines.flow.collect
40+
import kotlinx.coroutines.launch
4141
import javax.inject.Inject
4242

4343
@AndroidEntryPoint
@@ -89,30 +89,28 @@ class ScheduleTwoPaneFragment : MainNavigationFragment() {
8989
scheduleTwoPaneViewModel.setIsTwoPane(!binding.slidingPaneLayout.isSlideable)
9090
}
9191

92-
scheduleTwoPaneViewModel.navigateToSessionAction.observe(
93-
viewLifecycleOwner,
94-
EventObserver { sessionId ->
95-
detailPaneNavController.navigate(
96-
ScheduleDetailNavGraphDirections.toSessionDetail(sessionId)
97-
)
98-
// On narrow screens, slide the detail pane over the list pane if it isn't already
99-
// on top. If both panes are visible, this will have no effect.
100-
binding.slidingPaneLayout.open()
92+
launchAndRepeatWithViewLifecycle {
93+
launch {
94+
scheduleTwoPaneViewModel.selectSessionEvents.collect { sessionId ->
95+
detailPaneNavController.navigate(
96+
ScheduleDetailNavGraphDirections.toSessionDetail(sessionId)
97+
)
98+
// On narrow screens, slide the detail pane over the list pane if it isn't already
99+
// on top. If both panes are visible, this will have no effect.
100+
binding.slidingPaneLayout.open()
101+
}
101102
}
102-
)
103-
104-
lifecycleScope.launchWhenStarted {
105-
scheduleTwoPaneViewModel.returnToListPaneEvents.collect {
106-
binding.slidingPaneLayout.close()
103+
launch {
104+
scheduleTwoPaneViewModel.navigateToSignInDialogEvents.collect {
105+
openSignInDialog()
106+
}
107107
}
108-
}
109-
110-
scheduleTwoPaneViewModel.navigateToSignInDialogAction.observe(
111-
viewLifecycleOwner,
112-
EventObserver {
113-
openSignInDialog()
108+
launch {
109+
scheduleTwoPaneViewModel.returnToListPaneEvents.collect {
110+
binding.slidingPaneLayout.close()
111+
}
114112
}
115-
)
113+
}
116114

117115
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, backPressHandler)
118116
}

mobile/src/main/java/com/google/samples/apps/iosched/ui/schedule/ScheduleTwoPaneViewModel.kt

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@
1717
package com.google.samples.apps.iosched.ui.schedule
1818

1919
import androidx.lifecycle.ViewModel
20+
import com.google.samples.apps.iosched.model.SessionId
2021
import com.google.samples.apps.iosched.shared.util.tryOffer
21-
import com.google.samples.apps.iosched.ui.sessioncommon.EventActionsViewModelDelegate
22+
import com.google.samples.apps.iosched.ui.sessioncommon.OnSessionClickListener
23+
import com.google.samples.apps.iosched.ui.sessioncommon.OnSessionStarClickDelegate
2224
import dagger.hilt.android.lifecycle.HiltViewModel
2325
import kotlinx.coroutines.channels.Channel
2426
import kotlinx.coroutines.flow.MutableStateFlow
@@ -29,20 +31,29 @@ import javax.inject.Inject
2931
// Note: clients should obtain this from the Activity.
3032
@HiltViewModel
3133
class ScheduleTwoPaneViewModel @Inject constructor(
32-
eventActionsViewModelDelegate: EventActionsViewModelDelegate
33-
) : ViewModel(), EventActionsViewModelDelegate by eventActionsViewModelDelegate {
34+
onSessionStarClickDelegate: OnSessionStarClickDelegate
35+
) : ViewModel(),
36+
OnSessionClickListener,
37+
OnSessionStarClickDelegate by onSessionStarClickDelegate {
3438

3539
private val _isTwoPane = MutableStateFlow(false)
3640
val isTwoPane: StateFlow<Boolean> = _isTwoPane
3741

3842
private val _returnToListPaneEvents = Channel<Unit>(capacity = Channel.CONFLATED)
3943
val returnToListPaneEvents = _returnToListPaneEvents.receiveAsFlow()
4044

45+
private val _selectSessionEvents = Channel<SessionId>(capacity = Channel.CONFLATED)
46+
val selectSessionEvents = _selectSessionEvents.receiveAsFlow()
47+
4148
fun setIsTwoPane(isTwoPane: Boolean) {
4249
_isTwoPane.value = isTwoPane
4350
}
4451

4552
fun returnToListPane() {
4653
_returnToListPaneEvents.tryOffer(Unit)
4754
}
55+
56+
override fun openEventDetail(id: SessionId) {
57+
_selectSessionEvents.tryOffer(id)
58+
}
4859
}

mobile/src/main/java/com/google/samples/apps/iosched/ui/schedule/ScheduleViewModel.kt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ class ScheduleViewModel @Inject constructor(
7979
private val snackbarMessageManager: SnackbarMessageManager,
8080
getTimeZoneUseCase: GetTimeZoneUseCase,
8181
private val refreshConferenceDataUseCase: RefreshConferenceDataUseCase,
82-
observeConferenceDataUseCase: ObserveConferenceDataUseCase,
82+
observeConferenceDataUseCase: ObserveConferenceDataUseCase
8383
) : ViewModel(),
8484
SignInViewModelDelegate by signInViewModelDelegate {
8585

@@ -211,9 +211,7 @@ class ScheduleViewModel @Inject constructor(
211211
// Setting smoothScroll to false as it's an unnecessary delay.
212212
emit(ScheduleScrollEvent(currentEventIndex, smoothScroll = false))
213213
} else {
214-
val index =
215-
(result as? Success)?.data?.firstUnfinishedSessionIndex
216-
?: return@combineTransform
214+
val index = result.data?.firstUnfinishedSessionIndex ?: return@combineTransform
217215
if (index != -1) {
218216
emit(ScheduleScrollEvent(index))
219217
}

mobile/src/main/java/com/google/samples/apps/iosched/ui/search/SearchFragment.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,11 +113,12 @@ class SearchFragment : Fragment() {
113113
}
114114

115115
sessionsAdapter = SessionsAdapter(
116-
scheduleTwoPaneViewModel,
117116
tagViewPool,
118117
viewModel.showReservations,
119118
viewModel.timeZoneId,
120-
viewLifecycleOwner
119+
viewLifecycleOwner,
120+
scheduleTwoPaneViewModel, // OnSessionClickListener
121+
scheduleTwoPaneViewModel // OnSessionStarClickListener
121122
)
122123
binding.recyclerView.apply {
123124
adapter = sessionsAdapter

mobile/src/main/java/com/google/samples/apps/iosched/ui/sessioncommon/EventActions.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ import com.google.samples.apps.iosched.model.userdata.UserSession
2222
/**
2323
* Actions that can be performed on events.
2424
*/
25-
interface EventActions {
25+
interface OnSessionClickListener {
2626
fun openEventDetail(id: SessionId)
27+
}
28+
29+
interface OnSessionStarClickListener {
2730
fun onStarClicked(userSession: UserSession)
2831
}
Lines changed: 13 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,63 +16,53 @@
1616

1717
package com.google.samples.apps.iosched.ui.sessioncommon
1818

19-
import androidx.lifecycle.LiveData
20-
import androidx.lifecycle.MutableLiveData
2119
import com.google.samples.apps.iosched.R
22-
import com.google.samples.apps.iosched.model.SessionId
2320
import com.google.samples.apps.iosched.model.userdata.UserSession
2421
import com.google.samples.apps.iosched.shared.analytics.AnalyticsActions
2522
import com.google.samples.apps.iosched.shared.analytics.AnalyticsHelper
2623
import com.google.samples.apps.iosched.shared.di.ApplicationScope
2724
import com.google.samples.apps.iosched.shared.di.MainDispatcher
2825
import com.google.samples.apps.iosched.shared.domain.users.StarEventAndNotifyUseCase
2926
import com.google.samples.apps.iosched.shared.domain.users.StarEventParameter
30-
import com.google.samples.apps.iosched.shared.result.Event
3127
import com.google.samples.apps.iosched.shared.result.Result
28+
import com.google.samples.apps.iosched.shared.util.tryOffer
3229
import com.google.samples.apps.iosched.ui.messages.SnackbarMessage
3330
import com.google.samples.apps.iosched.ui.messages.SnackbarMessageManager
3431
import com.google.samples.apps.iosched.ui.signin.SignInViewModelDelegate
3532
import kotlinx.coroutines.CoroutineDispatcher
3633
import kotlinx.coroutines.CoroutineScope
34+
import kotlinx.coroutines.channels.Channel
35+
import kotlinx.coroutines.flow.Flow
36+
import kotlinx.coroutines.flow.receiveAsFlow
3737
import kotlinx.coroutines.launch
3838
import timber.log.Timber
3939
import java.util.UUID
4040
import javax.inject.Inject
4141

4242
/**
43-
* A delegate providing common functionality for displaying a list of events and responding to
44-
* actions performed on them.
43+
* A delegate providing common functionality for starring events.
4544
*/
46-
interface EventActionsViewModelDelegate : EventActions {
47-
val navigateToSessionAction: LiveData<Event<SessionId>>
48-
val navigateToSignInDialogAction: LiveData<Event<Unit>>
45+
46+
interface OnSessionStarClickDelegate : OnSessionStarClickListener {
47+
val navigateToSignInDialogEvents: Flow<Unit>
4948
}
5049

51-
class DefaultEventActionsViewModelDelegate @Inject constructor(
50+
class DefaultOnSessionStarClickDelegate @Inject constructor(
5251
signInViewModelDelegate: SignInViewModelDelegate,
5352
private val starEventUseCase: StarEventAndNotifyUseCase,
5453
private val snackbarMessageManager: SnackbarMessageManager,
5554
private val analyticsHelper: AnalyticsHelper,
5655
@ApplicationScope private val externalScope: CoroutineScope,
5756
@MainDispatcher private val mainDispatcher: CoroutineDispatcher
58-
) : EventActionsViewModelDelegate, SignInViewModelDelegate by signInViewModelDelegate {
59-
60-
private val _navigateToSessionAction = MutableLiveData<Event<SessionId>>()
61-
override val navigateToSessionAction: LiveData<Event<SessionId>>
62-
get() = _navigateToSessionAction
57+
) : OnSessionStarClickDelegate, SignInViewModelDelegate by signInViewModelDelegate {
6358

64-
private val _navigateToSignInDialogAction = MutableLiveData<Event<Unit>>()
65-
override val navigateToSignInDialogAction: LiveData<Event<Unit>>
66-
get() = _navigateToSignInDialogAction
67-
68-
override fun openEventDetail(id: SessionId) {
69-
_navigateToSessionAction.value = Event(id)
70-
}
59+
private val _navigateToSignInDialogEvents = Channel<Unit>(capacity = Channel.CONFLATED)
60+
override val navigateToSignInDialogEvents = _navigateToSignInDialogEvents.receiveAsFlow()
7161

7262
override fun onStarClicked(userSession: UserSession) {
7363
if (!isUserSignedInValue) {
7464
Timber.d("Showing Sign-in dialog after star click")
75-
_navigateToSignInDialogAction.value = Event(Unit)
65+
_navigateToSignInDialogEvents.tryOffer(Unit)
7666
return
7767
}
7868
val newIsStarredState = !userSession.userEvent.isStarred
@@ -90,7 +80,6 @@ class DefaultEventActionsViewModelDelegate @Inject constructor(
9080
requestChangeId = UUID.randomUUID().toString()
9181
)
9282
)
93-
9483
if (newIsStarredState) {
9584
analyticsHelper.logUiEvent(userSession.session.title, AnalyticsActions.STARRED)
9685
}
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,22 +30,22 @@ import kotlinx.coroutines.CoroutineDispatcher
3030
import kotlinx.coroutines.CoroutineScope
3131

3232
/**
33-
* Provides a default implementation of [EventActionsViewModelDelegate].
33+
* Provides a default implementation of [OnSessionStarClickDelegate].
3434
*/
3535
@InstallIn(ViewModelComponent::class)
3636
@Module
37-
internal class EventActionsViewModelDelegateModule {
37+
internal class OnSessionStarClickDelegateModule {
3838

3939
@Provides
40-
fun provideEventActionsViewModelDelegate(
40+
fun provideOnSessionStarClickDelegate(
4141
signInViewModelDelegate: SignInViewModelDelegate,
4242
starEventUseCase: StarEventAndNotifyUseCase,
4343
snackbarMessageManager: SnackbarMessageManager,
4444
analyticsHelper: AnalyticsHelper,
4545
@ApplicationScope applicationScope: CoroutineScope,
4646
@MainDispatcher mainDispatcher: CoroutineDispatcher
47-
): EventActionsViewModelDelegate {
48-
return DefaultEventActionsViewModelDelegate(
47+
): OnSessionStarClickDelegate {
48+
return DefaultOnSessionStarClickDelegate(
4949
signInViewModelDelegate,
5050
starEventUseCase,
5151
snackbarMessageManager,

0 commit comments

Comments
 (0)