Skip to content
This repository was archived by the owner on Nov 21, 2024. It is now read-only.

Commit 25f8ac4

Browse files
committed
Extract Lessons sheet to fragment & close it on back pressed.
Change-Id: I6dccca3a1ebadd4127a45dcb942ed304cf84081b
1 parent 9f014e1 commit 25f8ac4

File tree

5 files changed

+299
-210
lines changed

5 files changed

+299
-210
lines changed

app/src/main/java/com/materialstudies/owl/ui/learn/LearnFragment.kt

Lines changed: 2 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -16,158 +16,34 @@
1616

1717
package com.materialstudies.owl.ui.learn
1818

19-
import android.content.res.ColorStateList
20-
import android.graphics.Canvas
21-
import android.graphics.Paint
22-
import android.graphics.Rect
2319
import android.os.Bundle
2420
import android.view.LayoutInflater
2521
import android.view.View
26-
import android.view.View.GONE
27-
import android.view.View.VISIBLE
2822
import android.view.ViewGroup
29-
import androidx.annotation.ColorInt
30-
import androidx.annotation.Px
31-
import androidx.core.view.doOnLayout
32-
import androidx.core.view.forEach
3323
import androidx.fragment.app.Fragment
3424
import androidx.navigation.fragment.findNavController
35-
import androidx.recyclerview.widget.RecyclerView
36-
import com.google.android.material.bottomsheet.BottomSheetBehavior
37-
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_COLLAPSED
38-
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED
39-
import com.google.android.material.shape.MaterialShapeDrawable
40-
import com.google.android.material.shape.ShapeAppearanceModel
41-
import com.materialstudies.owl.R
4225
import com.materialstudies.owl.databinding.FragmentLearnBinding
4326
import com.materialstudies.owl.model.courses
44-
import com.materialstudies.owl.model.lessons
45-
import com.materialstudies.owl.util.doOnApplyWindowInsets
46-
import com.materialstudies.owl.util.lerp
47-
import com.materialstudies.owl.util.lerpArgb
4827

4928
/**
5029
* A [Fragment] displaying the learn screen.
5130
*/
5231
class LearnFragment : Fragment() {
5332

5433
override fun onCreateView(
55-
inflater: LayoutInflater, container: ViewGroup?,
34+
inflater: LayoutInflater,
35+
container: ViewGroup?,
5636
savedInstanceState: Bundle?
5737
): View? {
5838
val binding = FragmentLearnBinding.inflate(inflater, container, false).apply {
5939
course = courses.last()
60-
61-
val behavior = BottomSheetBehavior.from(lessonsSheet)
62-
val sheetStartColor = lessonsSheet.context.getColor(R.color.owl_pink_500)
63-
val sheetEndColor =
64-
lessonsSheet.context.getColorStateList(R.color.primary_sheet).defaultColor
65-
val sheetBackground = MaterialShapeDrawable(
66-
ShapeAppearanceModel(
67-
lessonsSheet.context, R.style.ShapeAppearance_Owl_MinimizedSheet, 0
68-
)
69-
).apply {
70-
fillColor = ColorStateList.valueOf(sheetStartColor)
71-
}
72-
lessonsSheet.background = sheetBackground
73-
lessonsSheet.doOnLayout {
74-
val peek = behavior.peekHeight
75-
val maxTranslationX = (it.width - peek).toFloat()
76-
lessonsSheet.translationX = (lessonsSheet.width - peek).toFloat()
77-
78-
// Alter views based on the sheet expansion
79-
behavior.setBottomSheetCallback(object :
80-
BottomSheetBehavior.BottomSheetCallback() {
81-
override fun onStateChanged(bottomSheet: View, newState: Int) {}
82-
override fun onSlide(bottomSheet: View, slideOffset: Float) {
83-
lessonsSheet.translationX =
84-
lerp(maxTranslationX, 0f, 0f, 0.15f, slideOffset)
85-
sheetBackground.interpolation = lerp(1f, 0f, 0f, 0.15f, slideOffset)
86-
sheetBackground.fillColor = ColorStateList.valueOf(
87-
lerpArgb(
88-
sheetStartColor,
89-
sheetEndColor,
90-
0f,
91-
0.3f,
92-
slideOffset
93-
)
94-
)
95-
playlistIcon.alpha = lerp(1f, 0f, 0f, 0.15f, slideOffset)
96-
sheetExpand.alpha = lerp(1f, 0f, 0f, 0.15f, slideOffset)
97-
sheetExpand.visibility = if (slideOffset < 0.5) VISIBLE else GONE
98-
playlistTitle.alpha = lerp(0f, 1f, 0.2f, 0.8f, slideOffset)
99-
collapsePlaylist.alpha = lerp(0f, 1f, 0.2f, 0.8f, slideOffset)
100-
playlistTitleDivider.alpha = lerp(0f, 1f, 0.2f, 0.8f, slideOffset)
101-
playlist.alpha = lerp(0f, 1f, 0.2f, 0.8f, slideOffset)
102-
}
103-
})
104-
lessonsSheet.doOnApplyWindowInsets { _, insets, _, _ ->
105-
behavior.peekHeight = peek + insets.systemWindowInsetBottom
106-
}
107-
}
108-
collapsePlaylist.setOnClickListener {
109-
behavior.state = STATE_COLLAPSED
110-
}
111-
sheetExpand.setOnClickListener {
112-
behavior.state = STATE_EXPANDED
113-
}
11440
toolbar.setNavigationOnClickListener {
11541
findNavController().popBackStack()
11642
}
117-
playlist.adapter = LessonAdapter().apply {
118-
submitList(lessons)
119-
}
120-
playlist.addItemDecoration(
121-
InsetDivider(
122-
resources.getDimensionPixelSize(R.dimen.divider_inset),
123-
resources.getDimensionPixelSize((R.dimen.divider_height)),
124-
playlist.context.getColor(R.color.divider)
125-
)
126-
)
12743
alsoLikeList.adapter = RelatedAdapter().apply {
12844
submitList(courses)
12945
}
13046
}
13147
return binding.root
13248
}
13349
}
134-
135-
/**
136-
* An [ItemDecoration] which is inset from the left by the given amount.
137-
*/
138-
class InsetDivider(
139-
@Px private val inset: Int,
140-
@Px private val height: Int,
141-
@ColorInt private val dividerColor: Int
142-
) : RecyclerView.ItemDecoration() {
143-
144-
private val dividerPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
145-
color = dividerColor
146-
style = Paint.Style.STROKE
147-
strokeWidth = height.toFloat()
148-
}
149-
150-
override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
151-
val points = mutableListOf<Float>()
152-
parent.forEach {
153-
if (parent.getChildAdapterPosition(it) < state.itemCount - 1) {
154-
val bottom = it.bottom.toFloat()
155-
points.add((it.left + inset).toFloat())
156-
points.add(bottom)
157-
points.add(it.right.toFloat())
158-
points.add(bottom)
159-
}
160-
}
161-
c.drawLines(points.toFloatArray(), dividerPaint)
162-
}
163-
164-
override fun getItemOffsets(
165-
outRect: Rect,
166-
view: View,
167-
parent: RecyclerView,
168-
state: RecyclerView.State
169-
) {
170-
outRect.top = height / 2
171-
outRect.bottom = height / 2
172-
}
173-
}
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
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.owl.ui.lessons
18+
19+
import android.content.res.ColorStateList
20+
import android.graphics.Canvas
21+
import android.graphics.Paint
22+
import android.graphics.Rect
23+
import android.os.Bundle
24+
import android.view.LayoutInflater
25+
import android.view.View
26+
import android.view.ViewGroup
27+
import androidx.activity.addCallback
28+
import androidx.annotation.ColorInt
29+
import androidx.annotation.Px
30+
import androidx.core.view.doOnLayout
31+
import androidx.core.view.forEach
32+
import androidx.fragment.app.Fragment
33+
import androidx.navigation.fragment.findNavController
34+
import androidx.recyclerview.widget.RecyclerView
35+
import com.google.android.material.bottomsheet.BottomSheetBehavior
36+
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED
37+
import com.google.android.material.shape.MaterialShapeDrawable
38+
import com.google.android.material.shape.ShapeAppearanceModel
39+
import com.materialstudies.owl.R
40+
import com.materialstudies.owl.databinding.FragmentLessonsSheetBinding
41+
import com.materialstudies.owl.model.courses
42+
import com.materialstudies.owl.model.lessons
43+
import com.materialstudies.owl.ui.learn.LessonAdapter
44+
import com.materialstudies.owl.util.doOnApplyWindowInsets
45+
import com.materialstudies.owl.util.lerp
46+
import com.materialstudies.owl.util.lerpArgb
47+
48+
/**
49+
* A [Fragment] displaying a list of lessons as a bottom sheet.
50+
*/
51+
class LessonsSheetFragment : Fragment() {
52+
53+
override fun onCreateView(
54+
inflater: LayoutInflater,
55+
container: ViewGroup?,
56+
savedInstanceState: Bundle?
57+
): View? {
58+
val binding = FragmentLessonsSheetBinding.inflate(inflater, container, false).apply {
59+
course = courses.last()
60+
val behavior = BottomSheetBehavior.from(lessonsSheet)
61+
val backCallback =
62+
requireActivity().onBackPressedDispatcher.addCallback(lifecycleOwner, false) {
63+
behavior.state = BottomSheetBehavior.STATE_COLLAPSED
64+
}
65+
val sheetStartColor = lessonsSheet.context.getColor(R.color.owl_pink_500)
66+
val sheetEndColor =
67+
lessonsSheet.context.getColorStateList(R.color.primary_sheet).defaultColor
68+
val sheetBackground = MaterialShapeDrawable(
69+
ShapeAppearanceModel(
70+
lessonsSheet.context, R.style.ShapeAppearance_Owl_MinimizedSheet, 0
71+
)
72+
).apply {
73+
fillColor = ColorStateList.valueOf(sheetStartColor)
74+
}
75+
lessonsSheet.background = sheetBackground
76+
lessonsSheet.doOnLayout {
77+
val peek = behavior.peekHeight
78+
val maxTranslationX = (it.width - peek).toFloat()
79+
lessonsSheet.translationX = (lessonsSheet.width - peek).toFloat()
80+
81+
// Alter views based on the sheet expansion
82+
behavior.setBottomSheetCallback(object :
83+
BottomSheetBehavior.BottomSheetCallback() {
84+
override fun onStateChanged(bottomSheet: View, newState: Int) {
85+
backCallback.isEnabled = newState == STATE_EXPANDED
86+
}
87+
88+
override fun onSlide(bottomSheet: View, slideOffset: Float) {
89+
lessonsSheet.translationX =
90+
lerp(maxTranslationX, 0f, 0f, 0.15f, slideOffset)
91+
sheetBackground.interpolation = lerp(1f, 0f, 0f, 0.15f, slideOffset)
92+
sheetBackground.fillColor = ColorStateList.valueOf(
93+
lerpArgb(
94+
sheetStartColor,
95+
sheetEndColor,
96+
0f,
97+
0.3f,
98+
slideOffset
99+
)
100+
)
101+
playlistIcon.alpha = lerp(1f, 0f, 0f, 0.15f, slideOffset)
102+
sheetExpand.alpha = lerp(1f, 0f, 0f, 0.15f, slideOffset)
103+
sheetExpand.visibility = if (slideOffset < 0.5) View.VISIBLE else View.GONE
104+
playlistTitle.alpha = lerp(0f, 1f, 0.2f, 0.8f, slideOffset)
105+
collapsePlaylist.alpha = lerp(0f, 1f, 0.2f, 0.8f, slideOffset)
106+
playlistTitleDivider.alpha = lerp(0f, 1f, 0.2f, 0.8f, slideOffset)
107+
playlist.alpha = lerp(0f, 1f, 0.2f, 0.8f, slideOffset)
108+
}
109+
})
110+
lessonsSheet.doOnApplyWindowInsets { _, insets, _, _ ->
111+
behavior.peekHeight = peek + insets.systemWindowInsetBottom
112+
}
113+
}
114+
collapsePlaylist.setOnClickListener {
115+
behavior.state = BottomSheetBehavior.STATE_COLLAPSED
116+
}
117+
sheetExpand.setOnClickListener {
118+
behavior.state = BottomSheetBehavior.STATE_EXPANDED
119+
}
120+
playlist.adapter = LessonAdapter().apply {
121+
submitList(lessons)
122+
}
123+
playlist.addItemDecoration(
124+
InsetDivider(
125+
resources.getDimensionPixelSize(R.dimen.divider_inset),
126+
resources.getDimensionPixelSize((R.dimen.divider_height)),
127+
playlist.context.getColor(R.color.divider)
128+
)
129+
)
130+
}
131+
return binding.root
132+
}
133+
}
134+
135+
/**
136+
* An [ItemDecoration] which is inset from the left by the given amount.
137+
*/
138+
class InsetDivider(
139+
@Px private val inset: Int,
140+
@Px private val height: Int,
141+
@ColorInt private val dividerColor: Int
142+
) : RecyclerView.ItemDecoration() {
143+
144+
private val dividerPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
145+
color = dividerColor
146+
style = Paint.Style.STROKE
147+
strokeWidth = height.toFloat()
148+
}
149+
150+
override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
151+
val points = mutableListOf<Float>()
152+
parent.forEach {
153+
if (parent.getChildAdapterPosition(it) < state.itemCount - 1) {
154+
val bottom = it.bottom.toFloat()
155+
points.add((it.left + inset).toFloat())
156+
points.add(bottom)
157+
points.add(it.right.toFloat())
158+
points.add(bottom)
159+
}
160+
}
161+
c.drawLines(points.toFloatArray(), dividerPaint)
162+
}
163+
164+
override fun getItemOffsets(
165+
outRect: Rect,
166+
view: View,
167+
parent: RecyclerView,
168+
state: RecyclerView.State
169+
) {
170+
outRect.top = height / 2
171+
outRect.bottom = height / 2
172+
}
173+
}

0 commit comments

Comments
 (0)