Skip to content
Permalink
Browse files

Make app go edge-to-edge

This CL makes the majority of the app draw from top
to bottom, behind the status and navigation bars.

I haven't touch session or speaker details yet,
that will come in a later CL.

The custom build of MDC is because it includes
an upcoming bug fix in AppBarLayout which will
be available in v1.1.0-alpha05.

Change-Id: I61ecf1ea6e17e1868388d18fbdd29f34f1559e8a
  • Loading branch information...
chrisbanes authored and thagikura committed Mar 8, 2019
1 parent 6c22545 commit 84ef129ef20cd87119edbb26ccaf1ab5aa58418b
Showing with 1,007 additions and 474 deletions.
  1. +2 −1 build.gradle
  2. +2 −4 mobile/src/main/AndroidManifest.xml
  3. +25 −2 mobile/src/main/java/com/google/samples/apps/iosched/ui/MainActivity.kt
  4. +7 −2 mobile/src/main/java/com/google/samples/apps/iosched/ui/agenda/AgendaFragment.kt
  5. +15 −0 mobile/src/main/java/com/google/samples/apps/iosched/ui/feed/FeedFragment.kt
  6. +8 −0 mobile/src/main/java/com/google/samples/apps/iosched/ui/info/EventFragment.kt
  7. +11 −0 mobile/src/main/java/com/google/samples/apps/iosched/ui/info/TravelFragment.kt
  8. +53 −0 mobile/src/main/java/com/google/samples/apps/iosched/ui/map/MapFragment.kt
  9. +7 −0 mobile/src/main/java/com/google/samples/apps/iosched/ui/schedule/ScheduleFragment.kt
  10. +51 −8 mobile/src/main/java/com/google/samples/apps/iosched/ui/schedule/filters/ScheduleFilterFragment.kt
  11. +43 −0 mobile/src/main/java/com/google/samples/apps/iosched/util/Extensions.kt
  12. +59 −0 mobile/src/main/java/com/google/samples/apps/iosched/util/StatusBarScrimBehavior.kt
  13. +37 −0 mobile/src/main/java/com/google/samples/apps/iosched/util/WindowInsetsListeners.kt
  14. +4 −2 mobile/src/main/res/layout-land/include_schedule_appbar.xml
  15. +24 −9 mobile/src/main/res/layout/activity_main.xml
  16. +5 −2 mobile/src/main/res/layout/fragment_agenda.xml
  17. +11 −2 mobile/src/main/res/layout/fragment_feed.xml
  18. +4 −2 mobile/src/main/res/layout/fragment_info.xml
  19. +263 −269 mobile/src/main/res/layout/fragment_info_event.xml
  20. +1 −0 mobile/src/main/res/layout/fragment_info_travel.xml
  21. +145 −126 mobile/src/main/res/layout/fragment_map.xml
  22. +10 −1 mobile/src/main/res/layout/fragment_schedule.xml
  23. +5 −1 mobile/src/main/res/layout/fragment_schedule_filter.xml
  24. +4 −2 mobile/src/main/res/layout/fragment_settings.xml
  25. +12 −15 mobile/src/main/res/layout/include_schedule_appbar.xml
  26. +5 −0 mobile/src/main/res/values-night/colors.xml
  27. +2 −1 mobile/src/main/res/values-notnight-v23/colors.xml
  28. +3 −8 mobile/src/main/res/values-notnight-v27/{styles.xml → colors.xml}
  29. +20 −0 mobile/src/main/res/values-notnight-v27/config.xml
  30. +8 −1 mobile/src/main/res/values/colors.xml
  31. +1 −0 mobile/src/main/res/values/config.xml
  32. +9 −15 mobile/src/main/res/values/styles.xml
  33. BIN ...google/android/material/material/1.1.0-alpha04-cl237931928/material-1.1.0-alpha04-cl237931928.aar
  34. +149 −0 ...google/android/material/material/1.1.0-alpha04-cl237931928/material-1.1.0-alpha04-cl237931928.pom
  35. +2 −1 shared/src/main/res/values/colors.xml
@@ -65,7 +65,7 @@ buildscript {
legacySupportVersion = '1.0.0'
lifecycleVersion = '2.0.0'
lottieVersion = "2.5.1"
materialVersion = '1.1.0-alpha04'
materialVersion = '1.1.0-alpha04-cl237931928'
mockitoVersion = "2.8.9"
mockitoKotlinVersion = "1.5.0"
navigationVersion = "2.0.0-rc02"
@@ -121,6 +121,7 @@ allprojects {
maven {
url "${rootProject.projectDir}/../../../prebuilts/fullsdk/linux/extras/support/m2repository"
}
maven { url "${rootProject.projectDir}/prebuilts/m2repository" }

flatDir {
dirs "libs"
@@ -59,8 +59,7 @@
</activity>

<activity
android:name=".ui.MainActivity"
android:theme="@style/AppTheme.NoActionBar" />
android:name=".ui.MainActivity" />

<activity
android:name=".ui.onboarding.OnboardingActivity"
@@ -77,8 +76,7 @@
android:theme="@style/AppTheme.Speaker" />

<activity
android:name=".ui.map.MapActivity"
android:theme="@style/AppTheme.NoActionBar" />
android:name=".ui.map.MapActivity" />

<!-- This activity alias lets us change the main entry point without breaking launcher
shortcuts. DO NOT change its android:name attribute. -->
@@ -20,8 +20,10 @@ import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.view.View
import android.widget.FrameLayout
import android.widget.Toast
import androidx.core.view.GravityCompat
import androidx.core.view.updatePadding
import androidx.drawerlayout.widget.DrawerLayout
import androidx.drawerlayout.widget.DrawerLayout.DrawerListener
import androidx.lifecycle.Observer
@@ -30,9 +32,9 @@ import com.firebase.ui.auth.IdpResponse
import com.google.android.material.navigation.NavigationView
import com.google.samples.apps.iosched.R
import com.google.samples.apps.iosched.databinding.NavigationHeaderBinding
import com.google.samples.apps.iosched.shared.di.MapFeatureEnabledFlag
import com.google.samples.apps.iosched.shared.di.ExploreArEnabledFlag
import com.google.samples.apps.iosched.shared.di.FeedFeatureEnabledFlag
import com.google.samples.apps.iosched.shared.di.MapFeatureEnabledFlag
import com.google.samples.apps.iosched.shared.result.EventObserver
import com.google.samples.apps.iosched.shared.util.inTransaction
import com.google.samples.apps.iosched.shared.util.viewModelProvider
@@ -45,6 +47,9 @@ import com.google.samples.apps.iosched.ui.schedule.ScheduleFragment
import com.google.samples.apps.iosched.ui.settings.SettingsFragment
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.NoopWindowInsetsListener
import com.google.samples.apps.iosched.util.doOnApplyWindowInsets
import com.google.samples.apps.iosched.util.signin.FirebaseAuthErrorCodeConverter
import com.google.samples.apps.iosched.util.updateForTheme
import dagger.android.support.DaggerAppCompatActivity
@@ -90,7 +95,10 @@ class MainActivity : DaggerAppCompatActivity(), NavigationHost, DrawerListener {

private lateinit var drawer: DrawerLayout
private lateinit var navigation: NavigationView
private lateinit var statusScrim: View

private lateinit var navHeaderBinding: NavigationHeaderBinding
private lateinit var content: FrameLayout

private lateinit var currentFragment: MainNavigationFragment
private var currentNavId = NAV_ID_NONE
@@ -104,10 +112,20 @@ class MainActivity : DaggerAppCompatActivity(), NavigationHost, DrawerListener {
updateForTheme(viewModel.currentTheme)

setContentView(R.layout.activity_main)
drawer = findViewById(R.id.drawer)

drawer = findViewById(R.id.drawer)
drawer.addDrawerListener(this)

content = findViewById(R.id.content_container)
content.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
// Make the content ViewGroup ignore insets so that it does not use the default padding
content.setOnApplyWindowInsetsListener(NoopWindowInsetsListener)

statusScrim = findViewById(R.id.status_bar_scrim)
statusScrim.setOnApplyWindowInsetsListener(HeightTopWindowInsetsListener)

navHeaderBinding = NavigationHeaderBinding.inflate(layoutInflater).apply {
viewModel = this@MainActivity.viewModel
lifecycleOwner = this@MainActivity
@@ -127,6 +145,11 @@ class MainActivity : DaggerAppCompatActivity(), NavigationHost, DrawerListener {
}
}

// Update the Navigation header view to pad itself down
navHeaderBinding.root.doOnApplyWindowInsets { v, insets, padding ->
v.updatePadding(top = padding.top + insets.systemWindowInsetTop)
}

if (savedInstanceState == null) {
// default to showing Schedule
val initialNavId = intent.getIntExtra(EXTRA_NAVIGATION_ID, R.id.navigation_schedule)
@@ -20,6 +20,7 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.updatePadding
import androidx.databinding.BindingAdapter
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.RecyclerView
@@ -28,6 +29,7 @@ import com.google.samples.apps.iosched.model.Block
import com.google.samples.apps.iosched.shared.util.viewModelProvider
import com.google.samples.apps.iosched.ui.MainNavigationFragment
import com.google.samples.apps.iosched.util.clearDecorations
import com.google.samples.apps.iosched.util.doOnApplyWindowInsets
import org.threeten.bp.ZoneId
import javax.inject.Inject

@@ -46,6 +48,10 @@ class AgendaFragment : MainNavigationFragment() {
binding = FragmentAgendaBinding.inflate(inflater, container, false).apply {
lifecycleOwner = viewLifecycleOwner
}
// Pad the bottom of the RecyclerView so that the content scrolls up above the nav bar
binding.recyclerView.doOnApplyWindowInsets { v, insets, padding ->
v.updatePadding(bottom = padding.bottom + insets.systemWindowInsetBottom)
}
return binding.root
}

@@ -61,11 +67,10 @@ fun agendaItems(recyclerView: RecyclerView, list: List<Block>?, timeZoneId: Zone
if (recyclerView.adapter == null) {
recyclerView.adapter = AgendaAdapter()
}
val adapter = (recyclerView.adapter as AgendaAdapter).apply {
(recyclerView.adapter as AgendaAdapter).apply {
this.submitList(list ?: emptyList())
this.timeZoneId = timeZoneId ?: ZoneId.systemDefault()
}

// Recreate the decoration used for the sticky date headers
recyclerView.clearDecorations()
if (list != null && list.isNotEmpty()) {
@@ -21,6 +21,8 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import androidx.databinding.BindingAdapter
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
@@ -34,6 +36,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.setUpSnackbar
import com.google.samples.apps.iosched.util.doOnApplyWindowInsets
import kotlinx.android.synthetic.main.fragment_feed.toolbar
import timber.log.Timber
import javax.inject.Inject
@@ -63,6 +66,18 @@ class FeedFragment : MainNavigationFragment() {
viewModel = model
}

binding.root.doOnApplyWindowInsets { _, insets, _ ->
binding.statusBar.run {
layoutParams.height = insets.systemWindowInsetTop
isVisible = layoutParams.height > 0
requestLayout()
}
}

binding.recyclerView.doOnApplyWindowInsets { v, insets, padding ->
v.updatePadding(bottom = padding.bottom + insets.systemWindowInsetBottom)
}

setUpSnackbar(model.snackBarMessage, binding.snackbar, snackbarMessageManager)

model.errorMessage.observe(this, Observer { message ->
@@ -24,6 +24,7 @@ import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.ViewGroup
import androidx.core.view.updatePaddingRelative
import androidx.databinding.BindingAdapter
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
@@ -33,6 +34,7 @@ import com.google.samples.apps.iosched.shared.util.TimeUtils
import com.google.samples.apps.iosched.shared.util.viewModelProvider
import com.google.samples.apps.iosched.ui.messages.SnackbarMessageManager
import com.google.samples.apps.iosched.ui.setUpSnackbar
import com.google.samples.apps.iosched.util.doOnApplyWindowInsets
import com.google.samples.apps.iosched.widget.FadingSnackbar
import dagger.android.support.DaggerFragment
import javax.inject.Inject
@@ -54,6 +56,12 @@ class EventFragment : DaggerFragment() {
viewModel = eventInfoViewModel
lifecycleOwner = viewLifecycleOwner
}

// Pad the bottom of the content so that it is above the nav bar
binding.content.doOnApplyWindowInsets { v, insets, padding ->
v.updatePaddingRelative(bottom = padding.bottom + insets.systemWindowInsetBottom)
}

val snackbarLayout = requireActivity().findViewById<FadingSnackbar>(R.id.snackbar)
setUpSnackbar(eventInfoViewModel.snackBarMessage, snackbarLayout, snackbarMessageManager)

@@ -20,7 +20,9 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.updatePaddingRelative
import com.google.samples.apps.iosched.R
import com.google.samples.apps.iosched.util.doOnApplyWindowInsets
import dagger.android.support.DaggerFragment

class TravelFragment : DaggerFragment() {
@@ -32,4 +34,13 @@ class TravelFragment : DaggerFragment() {
): View? {
return inflater.inflate(R.layout.fragment_info_travel, container, false)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

// Pad the bottom of the ScrollView so that it scrolls up above the nav bar
view.doOnApplyWindowInsets { v, insets, padding ->
v.updatePaddingRelative(bottom = padding.bottom + insets.systemWindowInsetBottom)
}
}
}
@@ -17,6 +17,8 @@
package com.google.samples.apps.iosched.ui.map

import android.Manifest
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.app.Dialog
import android.content.pm.PackageManager
import android.os.Bundle
@@ -25,7 +27,12 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.Toolbar
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding
import androidx.core.widget.NestedScrollView
import androidx.fragment.app.DialogFragment
import androidx.lifecycle.Observer
@@ -38,6 +45,7 @@ import com.google.samples.apps.iosched.databinding.FragmentMapBinding
import com.google.samples.apps.iosched.shared.analytics.AnalyticsHelper
import com.google.samples.apps.iosched.shared.util.viewModelProvider
import com.google.samples.apps.iosched.ui.MainNavigationFragment
import com.google.samples.apps.iosched.util.doOnApplyWindowInsets
import com.google.samples.apps.iosched.widget.BottomSheetBehavior
import com.google.samples.apps.iosched.widget.BottomSheetBehavior.BottomSheetCallback
import org.threeten.bp.Instant
@@ -124,13 +132,58 @@ class MapFragment : MainNavigationFragment() {
}
binding.expandIcon.animate().rotationX(rotation).start()

when (newState) {
BottomSheetBehavior.STATE_EXPANDED -> {
binding.descriptionScrollview.animate()
.setDuration(150)
.alpha(1f)
.setListener(object : AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator) {
binding.descriptionScrollview.isVisible = true
}
})
.start()
}
BottomSheetBehavior.STATE_COLLAPSED,
BottomSheetBehavior.STATE_HIDDEN -> {
binding.descriptionScrollview.animate()
.alpha(0f)
.setDuration(150)
.setListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
binding.descriptionScrollview.isInvisible = true
}
})
.start()
}
}

// Analytics
if (newState == BottomSheetBehavior.STATE_EXPANDED) {
viewModel.logViewedMarkerDetails()
}
}
})

binding.root.doOnApplyWindowInsets { _, insets, _ ->
binding.statusBar.updateLayoutParams<ConstraintLayout.LayoutParams> {
height = insets.systemWindowInsetTop
}
binding.statusBar.isVisible = true
binding.statusBar.requestLayout()

// Update the Map padding so that the copyright, etc is not displayed in nav bar
binding.map.getMapAsync {
it.setPadding(0, 0, 0, insets.systemWindowInsetBottom)
}
}

val originalPeekHeight = bottomSheetBehavior.peekHeight
binding.bottomSheet.doOnApplyWindowInsets { v, insets, _ ->
v.updatePadding(bottom = insets.systemWindowInsetBottom)
bottomSheetBehavior.peekHeight = insets.systemWindowInsetBottom + originalPeekHeight
}

// Make the header clickable.
binding.clickable.setOnClickListener {
if (bottomSheetBehavior.state == BottomSheetBehavior.STATE_COLLAPSED) {
@@ -23,6 +23,7 @@ import android.view.ViewGroup
import android.widget.Toast
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.DefaultItemAnimator
@@ -50,6 +51,7 @@ import com.google.samples.apps.iosched.ui.signin.NotificationsPreferenceDialogFr
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.clearDecorations
import com.google.samples.apps.iosched.util.doOnApplyWindowInsets
import com.google.samples.apps.iosched.util.executeAfter
import com.google.samples.apps.iosched.util.fabVisibility
import com.google.samples.apps.iosched.widget.BottomSheetBehavior
@@ -149,6 +151,11 @@ class ScheduleFragment : MainNavigationFragment() {
}
})

// Pad the bottom of the RecyclerView so that the content scrolls up above the nav bar
binding.recyclerview.doOnApplyWindowInsets { v, insets, padding ->
v.updatePadding(bottom = padding.bottom + insets.systemWindowInsetBottom)
}

// Session list configuration
scheduleAdapter = ScheduleAdapter(
scheduleViewModel,

0 comments on commit 84ef129

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