Skip to content
Permalink
Browse files

Update Session Details and sub-navigation structure to use the naviga…

…tion component.

The session detail and speaker screens are part of the nav graph.
The schedule and feed screens can launch the session details using the nav controller.
The session detail screen can launch other session details (related sessions) and speaker profile pages.
Shared element transitions have been moved from Activity level to Fragment level. Since multiple speakers can appear in session details, the speaker's id became the shared element key. Upon returning from the speaker screen to the session detail screen, if the speaker is not in view, the shared element transition will be skipped.
Handling launching the map from the session details screen is out of scope for this CL.
In a follow up CL, the SessionDetailActivity will be deleted. In the meantime, the SessionDetail fragment assumes that it can be launched from the SessionDetailActivity or from the nav controller.

Bug: 123673505
Bug: 129550095
Change-Id: I465dcfa7de33f89fe8c7128bd49fc33668b6eeef
  • Loading branch information...
benbaxter authored and thagikura committed Mar 29, 2019
1 parent a65ffba commit 4baf5b5f4daa2a2d53e65a96b59dd90f80633a45
@@ -88,11 +88,6 @@
</intent-filter>
</activity>

<activity
android:name=".ui.speaker.SpeakerActivity"
android:parentActivityName=".ui.MainActivity"
android:theme="@style/AppTheme.Speaker" />

<activity
android:name=".ui.map.MapActivity" />

@@ -34,10 +34,9 @@ import com.google.samples.apps.iosched.ui.schedule.ScheduleModule
import com.google.samples.apps.iosched.ui.sessioncommon.EventActionsViewModelDelegateModule
import com.google.samples.apps.iosched.ui.sessiondetail.SessionDetailActivity
import com.google.samples.apps.iosched.ui.sessiondetail.SessionDetailModule
import com.google.samples.apps.iosched.ui.settings.SettingsModule
import com.google.samples.apps.iosched.ui.sessiondetail.SessionFeedbackModule
import com.google.samples.apps.iosched.ui.settings.SettingsModule
import com.google.samples.apps.iosched.ui.signin.SignInDialogModule
import com.google.samples.apps.iosched.ui.speaker.SpeakerActivity
import com.google.samples.apps.iosched.ui.speaker.SpeakerModule
import dagger.Module
import dagger.android.ContributesAndroidInjector
@@ -74,7 +73,12 @@ abstract class ActivityBindingModule {
SettingsModule::class,
SignInDialogModule::class,
ReservationModule::class,
PreferenceModule::class
PreferenceModule::class,
SessionDetailModule::class,
SessionFeedbackModule::class,
SpeakerModule::class,
SignInDialogModule::class,
EventActionsViewModelDelegateModule::class
]
)
internal abstract fun mainActivity(): MainActivity
@@ -91,17 +95,6 @@ abstract class ActivityBindingModule {
)
internal abstract fun sessionDetailActivity(): SessionDetailActivity

@ActivityScoped
@ContributesAndroidInjector(
modules = [
SpeakerModule::class,
SignInDialogModule::class,
EventActionsViewModelDelegateModule::class,
PreferenceModule::class
]
)
internal abstract fun speakerActivity(): SpeakerActivity

@ActivityScoped
@ContributesAndroidInjector(
modules = [
@@ -275,7 +275,7 @@ class MainActivity : DaggerAppCompatActivity(), NavigationHost {
private fun getCurrentFragment(): MainNavigationFragment? {
return navHostFragment
?.childFragmentManager
?.primaryNavigationFragment as MainNavigationFragment?
?.primaryNavigationFragment as? MainNavigationFragment
}

private fun navigateTo(navId: Int) {
@@ -26,6 +26,7 @@ import androidx.core.view.updatePaddingRelative
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.RecyclerView
import com.google.common.collect.ImmutableMap
import com.google.samples.apps.iosched.R
@@ -34,9 +35,9 @@ import com.google.samples.apps.iosched.model.SessionId
import com.google.samples.apps.iosched.shared.result.EventObserver
import com.google.samples.apps.iosched.shared.util.viewModelProvider
import com.google.samples.apps.iosched.ui.MainNavigationFragment
import com.google.samples.apps.iosched.ui.feed.FeedFragmentDirections.Companion.toSessionDetail
import com.google.samples.apps.iosched.ui.messages.SnackbarMessageManager
import com.google.samples.apps.iosched.ui.schedule.ScheduleFragmentArgs
import com.google.samples.apps.iosched.ui.sessiondetail.SessionDetailActivity
import com.google.samples.apps.iosched.ui.setUpSnackbar
import com.google.samples.apps.iosched.util.doOnApplyWindowInsets
import com.google.samples.apps.iosched.util.getTappableElementInsetsAsRect
@@ -113,7 +114,7 @@ class FeedFragment : MainNavigationFragment() {
}

private fun openSessionDetail(id: SessionId) {
startActivity(SessionDetailActivity.starterIntent(requireContext(), id))
findNavController().navigate(toSessionDetail(id))
}

private fun openSchedule(withPinnedSessions: Boolean) {
@@ -27,6 +27,7 @@ import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
@@ -45,7 +46,7 @@ import com.google.samples.apps.iosched.shared.util.viewModelProvider
import com.google.samples.apps.iosched.ui.MainNavigationFragment
import com.google.samples.apps.iosched.ui.messages.SnackbarMessageManager
import com.google.samples.apps.iosched.ui.prefs.SnackbarPreferenceViewModel
import com.google.samples.apps.iosched.ui.sessiondetail.SessionDetailActivity
import com.google.samples.apps.iosched.ui.schedule.ScheduleFragmentDirections.Companion.toSessionDetail
import com.google.samples.apps.iosched.ui.setUpSnackbar
import com.google.samples.apps.iosched.ui.signin.NotificationsPreferenceDialogFragment
import com.google.samples.apps.iosched.ui.signin.NotificationsPreferenceDialogFragment.Companion.DIALOG_NOTIFICATIONS_PREFERENCE
@@ -385,7 +386,7 @@ class ScheduleFragment : MainNavigationFragment() {
}

private fun openSessionDetail(id: SessionId) {
startActivity(SessionDetailActivity.starterIntent(requireContext(), id))
findNavController().navigate(toSessionDetail(id))
}

private fun openSignInDialog() {
@@ -16,10 +16,10 @@

package com.google.samples.apps.iosched.ui.sessiondetail

import android.app.ActivityOptions
import android.content.Intent
import android.os.Bundle
import android.provider.CalendarContract
import android.view.ContextThemeWrapper
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -32,9 +32,13 @@ import androidx.core.view.forEach
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.FragmentNavigatorExtras
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.RecyclerView.RecycledViewPool
import androidx.transition.TransitionInflater
import com.google.android.material.bottomappbar.BottomAppBar
import com.google.samples.apps.iosched.R
import com.google.samples.apps.iosched.R.style
import com.google.samples.apps.iosched.databinding.FragmentSessionDetailBinding
import com.google.samples.apps.iosched.model.Session
import com.google.samples.apps.iosched.model.SessionId
@@ -55,12 +59,14 @@ import com.google.samples.apps.iosched.ui.reservation.RemoveReservationDialogFra
import com.google.samples.apps.iosched.ui.reservation.RemoveReservationDialogParameters
import com.google.samples.apps.iosched.ui.reservation.SwapReservationDialogFragment
import com.google.samples.apps.iosched.ui.reservation.SwapReservationDialogFragment.Companion.DIALOG_SWAP_RESERVATION
import com.google.samples.apps.iosched.ui.schedule.ScheduleFragmentDirections.Companion.toSessionDetail
import com.google.samples.apps.iosched.ui.sessiondetail.SessionDetailFragmentDirections.Companion.toSpeakerDetail
import com.google.samples.apps.iosched.ui.setUpSnackbar
import com.google.samples.apps.iosched.ui.signin.NotificationsPreferenceDialogFragment
import com.google.samples.apps.iosched.ui.signin.NotificationsPreferenceDialogFragment.Companion.DIALOG_NOTIFICATIONS_PREFERENCE
import com.google.samples.apps.iosched.ui.signin.SignInDialogFragment
import com.google.samples.apps.iosched.ui.signin.SignInDialogFragment.Companion.DIALOG_SIGN_IN
import com.google.samples.apps.iosched.ui.speaker.SpeakerActivity
import com.google.samples.apps.iosched.util.postponeEnterTransition
import dagger.android.support.DaggerFragment
import timber.log.Timber
import javax.inject.Inject
@@ -98,7 +104,14 @@ class SessionDetailFragment : DaggerFragment() {
): View? {
sessionDetailViewModel = viewModelProvider(viewModelFactory)

val binding = FragmentSessionDetailBinding.inflate(inflater, container, false).apply {
sharedElementReturnTransition =
TransitionInflater.from(context).inflateTransition(R.transition.speaker_shared_enter)
// Delay the enter transition until speaker image has loaded.
postponeEnterTransition(500L)

val themedInflater = inflater.cloneInContext(
ContextThemeWrapper(requireActivity(), style.AppTheme_SessionDetails))
val binding = FragmentSessionDetailBinding.inflate(themedInflater, container, false).apply {
viewModel = sessionDetailViewModel
lifecycleOwner = viewLifecycleOwner
}
@@ -136,7 +149,12 @@ class SessionDetailFragment : DaggerFragment() {
}

binding.up.setOnClickListener {
NavUtils.navigateUpFromSameTask(requireActivity())
// TODO(benbaxter): Remove try/catch once SessionDetailActivity is deleted.
try {
NavUtils.navigateUpFromSameTask(requireActivity())
} catch (e: Exception) {
findNavController().navigateUp()
}
}

val detailsAdapter = SessionDetailAdapter(
@@ -183,7 +201,7 @@ class SessionDetailFragment : DaggerFragment() {
})

sessionDetailViewModel.navigateToSessionAction.observe(this, EventObserver { sessionId ->
startActivity(SessionDetailActivity.starterIntent(requireContext(), sessionId))
findNavController().navigate(toSessionDetail(sessionId))
})

val snackbarPreferenceViewModel: SnackbarPreferenceViewModel =
@@ -219,14 +237,9 @@ class SessionDetailFragment : DaggerFragment() {
})

sessionDetailViewModel.navigateToSpeakerDetail.observe(this, EventObserver { speakerId ->
requireActivity().run {
val sharedElement =
findSpeakerHeadshot(binding.sessionDetailRecyclerView, speakerId)
val options = ActivityOptions.makeSceneTransitionAnimation(
this, sharedElement, getString(R.string.speaker_headshot_transition)
)
startActivity(SpeakerActivity.starterIntent(this, speakerId), options.toBundle())
}
val sharedElement = findSpeakerHeadshot(binding.sessionDetailRecyclerView, speakerId)
val extras = FragmentNavigatorExtras(sharedElement to sharedElement.transitionName)
findNavController().navigate(toSpeakerDetail(speakerId), extras)
})

sessionDetailViewModel.navigateToSessionFeedbackAction.observe(this, EventObserver {
@@ -240,7 +253,16 @@ class SessionDetailFragment : DaggerFragment() {
override fun onStart() {
super.onStart()
Timber.d("Loading details for session $arguments")
sessionDetailViewModel.setSessionId(requireNotNull(arguments).getString(EXTRA_SESSION_ID))

requireNotNull(arguments).apply {
// TODO(benbaxter): Only use SessionDetailFragmentArgs and delete SessionDetailActivity.
// Default with the value passed from the activity, otherwise assume the fragment was
// added from the navigation controller.
val sessionId = getString(EXTRA_SESSION_ID)
?: SessionDetailFragmentArgs.fromBundle(this).sessionId

sessionDetailViewModel.setSessionId(sessionId)
}
}

override fun onStop() {

This file was deleted.

@@ -90,13 +90,13 @@ fun speakerImage(

// Want a 'random' default avatar but should be stable as used on both session details &
// speaker detail screens (as a shared element transition), so use first initial to pick.
val placeholderId = when (speaker.name[0].toLowerCase()) {
val placeholderId = when (speaker.name.firstOrNull()?.toLowerCase()) {
in 'a'..'i' -> R.drawable.ic_default_avatar_1
in 'j'..'r' -> R.drawable.ic_default_avatar_2
else -> R.drawable.ic_default_avatar_3
}

if (speaker.imageUrl.isNullOrBlank()) {
if (speaker.imageUrl.isBlank()) {
imageView.setImageResource(placeholderId)
} else {
val imageLoad = Glide.with(imageView)

0 comments on commit 4baf5b5

Please sign in to comment.
You can’t perform that action at this time.