1
+ /*
2
+ * Copyright 2019 Google LLC
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * https://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ package com.materialstudies.reply.ui.nav
18
+
19
+ import android.content.res.ColorStateList
20
+ import android.os.Bundle
21
+ import android.view.LayoutInflater
22
+ import android.view.View
23
+ import android.view.ViewGroup
24
+ import android.widget.FrameLayout
25
+ import androidx.fragment.app.Fragment
26
+ import androidx.lifecycle.Observer
27
+ import com.google.android.material.bottomsheet.BottomSheetBehavior
28
+ import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_COLLAPSED
29
+ import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED
30
+ import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_HALF_EXPANDED
31
+ import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_HIDDEN
32
+ import com.google.android.material.bottomsheet.BottomSheetBehavior.from
33
+ import com.google.android.material.shape.MaterialShapeDrawable
34
+ import com.materialstudies.reply.App
35
+ import com.materialstudies.reply.R
36
+ import com.materialstudies.reply.data.EmailStore
37
+ import com.materialstudies.reply.databinding.FragmentBottomNavDrawerBinding
38
+ import com.materialstudies.reply.util.getColorFromAttr
39
+ import kotlin.LazyThreadSafetyMode.NONE
40
+
41
+ /* *
42
+ * A [Fragment] which acts as a bottom navigation drawer.
43
+ */
44
+ class BottomNavDrawerFragment : Fragment (), NavigationAdapter.NavigationAdapterListener {
45
+
46
+ private lateinit var emailStore: EmailStore
47
+ private lateinit var navigationModel: NavigationModel
48
+
49
+ private lateinit var binding: FragmentBottomNavDrawerBinding
50
+
51
+ private val behavior: BottomSheetBehavior <FrameLayout > by lazy(NONE ) {
52
+ from(binding.backgroundContainer)
53
+ }
54
+
55
+ private val bottomSheetCallback = BottomNavigationDrawerCallback ()
56
+
57
+ private val backgroundShapeDrawable: MaterialShapeDrawable by lazy(NONE ) {
58
+ MaterialShapeDrawable (
59
+ binding.backgroundContainer.context,
60
+ null ,
61
+ R .attr.bottomSheetStyle,
62
+ 0
63
+ ).apply {
64
+ fillColor = ColorStateList .valueOf(
65
+ requireContext().getColorFromAttr(R .attr.colorBrandedVariantSurface)
66
+ )
67
+ elevation = resources.getDimension(R .dimen.plane_08)
68
+ initializeElevationOverlay(requireContext())
69
+ }
70
+ }
71
+
72
+ private val foregroundShapeDrawable: MaterialShapeDrawable by lazy(NONE ) {
73
+ MaterialShapeDrawable (
74
+ binding.foregroundContainer.context,
75
+ null ,
76
+ R .attr.bottomSheetStyle,
77
+ 0
78
+ ).apply {
79
+ fillColor = ColorStateList .valueOf(
80
+ requireContext().getColorFromAttr(R .attr.colorBrandedSurface)
81
+ )
82
+ elevation = resources.getDimension(R .dimen.plane_16)
83
+ shadowCompatibilityMode = MaterialShapeDrawable .SHADOW_COMPAT_MODE_NEVER
84
+ initializeElevationOverlay(requireContext())
85
+ shapeAppearanceModel.topEdge = SemiCircleEdgeCutoutTreatment (
86
+ resources.getDimension(R .dimen.keyline_3),
87
+ resources.getDimension(R .dimen.keyline_5),
88
+ 0F ,
89
+ resources.getDimension(R .dimen.navigation_drawer_profile_image_size)
90
+ )
91
+ }
92
+ }
93
+
94
+ override fun onCreateView (
95
+ inflater : LayoutInflater ,
96
+ container : ViewGroup ? ,
97
+ savedInstanceState : Bundle ?
98
+ ): View ? {
99
+ binding = FragmentBottomNavDrawerBinding .inflate(inflater, container, false )
100
+ binding.foregroundContainer.setOnApplyWindowInsetsListener { view, windowInsets ->
101
+ // Record the window's top inset so it can be applied when the bottom sheet is slide up
102
+ // to meet the top edge of the screen.
103
+ view.setTag(
104
+ R .id.tag_system_window_inset_top,
105
+ windowInsets.systemWindowInsetTop
106
+ )
107
+ windowInsets
108
+ }
109
+ return binding.root
110
+ }
111
+
112
+ override fun onViewCreated (view : View , savedInstanceState : Bundle ? ) {
113
+ super .onViewCreated(view, savedInstanceState)
114
+ emailStore = (requireActivity().application as App ).emailStore
115
+ navigationModel = NavigationModel (emailStore)
116
+ binding.run {
117
+ backgroundContainer.background = backgroundShapeDrawable
118
+ foregroundContainer.background = foregroundShapeDrawable
119
+
120
+ scrimView.setOnClickListener { close() }
121
+
122
+ bottomSheetCallback.apply {
123
+ // Scrim view transforms
124
+ addOnSlideAction(AlphaSlideAction (scrimView))
125
+ addOnStateChangedAction(VisibilityStateAction (scrimView))
126
+ // Foreground transforms
127
+ addOnSlideAction(ForegroundSheetTransformSlideAction (
128
+ binding.foregroundContainer,
129
+ foregroundShapeDrawable,
130
+ binding.profileImageView
131
+ ))
132
+ // Recycler transforms
133
+ addOnStateChangedAction(ScrollToTopStateAction (navRecyclerView))
134
+ }
135
+
136
+ behavior.setBottomSheetCallback(bottomSheetCallback)
137
+ behavior.state = STATE_HIDDEN
138
+
139
+ val adapter = NavigationAdapter (this @BottomNavDrawerFragment)
140
+ binding.navRecyclerView.adapter = adapter
141
+ navigationModel.navigationList.observe(this @BottomNavDrawerFragment, Observer {
142
+ adapter.submitList(it)
143
+ })
144
+ navigationModel.setNavigationMenuItemChecked(0 )
145
+ }
146
+ }
147
+
148
+ fun toggle () {
149
+ when (behavior.state) {
150
+ STATE_HIDDEN -> open()
151
+ STATE_HALF_EXPANDED , STATE_EXPANDED , STATE_COLLAPSED -> close()
152
+ }
153
+ }
154
+
155
+ fun open () {
156
+ behavior.state = STATE_HALF_EXPANDED
157
+ }
158
+
159
+ fun close () {
160
+ behavior.state = STATE_HIDDEN
161
+ }
162
+
163
+ fun addOnSlideAction (action : OnSlideAction ) {
164
+ bottomSheetCallback.addOnSlideAction(action)
165
+ }
166
+
167
+ fun addOnStateChangedAction (action : OnStateChangedAction ) {
168
+ bottomSheetCallback.addOnStateChangedAction(action)
169
+ }
170
+
171
+ override fun onNavMenuItemClicked (item : NavigationModelItem .NavMenuItem ) {
172
+ if (navigationModel.setNavigationMenuItemChecked(item.id)) close()
173
+ }
174
+
175
+ override fun onNavEmailFolderClicked (folder : NavigationModelItem .NavEmailFolder ) {
176
+ // Do nothing
177
+ }
178
+ }
0 commit comments