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

Commit 5ed1a79

Browse files
committed
Add Onboarding screen.
Change-Id: I3a4c1b5d0bd08ae00ae82d55ba3329d268dc2d38
1 parent 88ae9ce commit 5ed1a79

37 files changed

+1034
-92
lines changed

app/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ dependencies {
5555
implementation "androidx.navigation:navigation-fragment-ktx:$navigation_version"
5656
implementation "androidx.navigation:navigation-ui:$navigation_version"
5757
implementation "androidx.navigation:navigation-ui-ktx:$navigation_version"
58-
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
58+
implementation 'androidx.dynamicanimation:dynamicanimation:1.0.0'
5959

6060
implementation 'com.github.bumptech.glide:glide:4.9.0'
6161
annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'

app/src/main/java/com/materialstudies/owl/model/Subject.kt

Lines changed: 0 additions & 43 deletions
This file was deleted.
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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.model
18+
19+
import androidx.recyclerview.widget.DiffUtil
20+
21+
data class Topic(
22+
val name: String,
23+
val courses: Int,
24+
val imageUrl: String
25+
)
26+
27+
object TopicDiff : DiffUtil.ItemCallback<Topic>() {
28+
override fun areItemsTheSame(oldItem: Topic, newItem: Topic) = oldItem.name == newItem.name
29+
override fun areContentsTheSame(oldItem: Topic, newItem: Topic) = oldItem == newItem
30+
}
31+
32+
val topics = listOf(
33+
Topic("Architecture", 58, "https://source.unsplash.com/RFDP7_80v5A"),
34+
Topic("Arts & Crafts", 121, "https://source.unsplash.com/Tq4YjCa2BSc"),
35+
Topic("Business", 78, "https://source.unsplash.com/MYbhN8KaaEc"),
36+
Topic("Culinary", 118, "https://source.unsplash.com/uB7q7aipU2o"),
37+
Topic("Design", 423, "https://source.unsplash.com/cXkrqY2wFyc"),
38+
Topic("Fashion", 92, "https://source.unsplash.com/omgRZCmTvUM"),
39+
Topic("Film", 165, "https://source.unsplash.com/9lTUAlNB87M"),
40+
Topic("Gaming", 164, "https://source.unsplash.com/xITnxxlzGAE"),
41+
Topic("Illustration", 326, "https://source.unsplash.com/lxoq0zppH5w"),
42+
Topic("Lifestyle", 305, "https://source.unsplash.com/DzIt-fTYv4E"),
43+
Topic("Music", 212, "https://source.unsplash.com/LoppUA_9F1w"),
44+
Topic("Painting", 172, "https://source.unsplash.com/FwF_fKj5tBo"),
45+
Topic("Photography", 321, "https://source.unsplash.com/8nXKXYdO-Wk"),
46+
Topic("Technology", 118, "https://source.unsplash.com/MxVkWPiJALs")
47+
)

app/src/main/java/com/materialstudies/owl/ui/MainActivity.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
package com.materialstudies.owl.ui
1818

1919
import android.os.Bundle
20+
import android.view.View.GONE
21+
import android.view.View.VISIBLE
2022
import androidx.appcompat.app.AppCompatActivity
2123
import androidx.navigation.Navigation
2224
import androidx.navigation.ui.setupWithNavController
@@ -33,6 +35,14 @@ class MainActivity : AppCompatActivity() {
3335
binding.apply {
3436
val navController = Navigation.findNavController(this@MainActivity, R.id.nav_host)
3537
bottomNav.setupWithNavController(navController)
38+
39+
// Hide bottom nav on screens which don't require it
40+
navController.addOnDestinationChangedListener { _, destination, _ ->
41+
bottomNav.visibility = when (destination.id) {
42+
R.id.myCourses, R.id.featured, R.id.search -> VISIBLE
43+
else -> GONE
44+
}
45+
}
3646
}
3747
}
3848
}

app/src/main/java/com/materialstudies/owl/ui/featured/FeaturedFragment.kt

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,11 @@
1616

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

19-
import android.graphics.Rect
2019
import android.os.Bundle
2120
import android.view.LayoutInflater
2221
import android.view.View
2322
import android.view.ViewGroup
24-
import androidx.annotation.Px
2523
import androidx.fragment.app.Fragment
26-
import androidx.recyclerview.widget.RecyclerView
27-
import com.materialstudies.owl.R
2824
import com.materialstudies.owl.databinding.FragmentFeaturedBinding
2925
import com.materialstudies.owl.model.courses
3026

@@ -36,26 +32,10 @@ class FeaturedFragment : Fragment() {
3632
savedInstanceState: Bundle?
3733
): View? {
3834
val binding = FragmentFeaturedBinding.inflate(inflater, container, false).apply {
39-
featuredGrid.apply {
40-
adapter = FeaturedAdapter().apply {
41-
submitList(courses)
42-
}
43-
addItemDecoration(
44-
OffsetDecoration(resources.getDimensionPixelSize(R.dimen.grid_0_5))
45-
)
35+
featuredGrid.adapter = FeaturedAdapter().apply {
36+
submitList(courses)
4637
}
4738
}
4839
return binding.root
4940
}
5041
}
51-
52-
class OffsetDecoration(@Px private val offset: Int) : RecyclerView.ItemDecoration() {
53-
override fun getItemOffsets(
54-
outRect: Rect,
55-
view: View,
56-
parent: RecyclerView,
57-
state: RecyclerView.State
58-
) {
59-
outRect.set(offset, offset, offset, offset)
60-
}
61-
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
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.onboarding
18+
19+
import android.os.Bundle
20+
import android.view.LayoutInflater
21+
import android.view.View
22+
import android.view.ViewGroup
23+
import androidx.annotation.Px
24+
import androidx.core.view.forEach
25+
import androidx.core.view.postDelayed
26+
import androidx.dynamicanimation.animation.SpringAnimation
27+
import androidx.fragment.app.Fragment
28+
import androidx.navigation.fragment.findNavController
29+
import androidx.recyclerview.widget.RecyclerView
30+
import androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_DRAGGING
31+
import com.materialstudies.owl.R
32+
import com.materialstudies.owl.databinding.FragmentOnboardingBinding
33+
import com.materialstudies.owl.model.topics
34+
import com.materialstudies.owl.util.spring
35+
36+
/**
37+
* Onboarding [Fragment] which displays a list of Topics for a user to select.
38+
*/
39+
class OnboardingFragment : Fragment() {
40+
41+
override fun onCreateView(
42+
inflater: LayoutInflater,
43+
container: ViewGroup?,
44+
savedInstanceState: Bundle?
45+
): View? {
46+
val binding = FragmentOnboardingBinding.inflate(inflater, container, false).apply {
47+
fab.setOnClickListener {
48+
findNavController().navigate(R.id.featured)
49+
}
50+
topicGrid.apply {
51+
adapter = TopicsAdapter(context).apply {
52+
// We're setting reverseLayout on the RV to layout from RTL, but we still want
53+
// data ordered LTR, so reverse it before setting
54+
submitList(topics.reversed())
55+
}
56+
smoothScrollToPosition(topics.size)
57+
addOnScrollListener(
58+
OscillatingScrollListener(resources.getDimensionPixelSize(R.dimen.grid_2))
59+
)
60+
}
61+
}
62+
return binding.root
63+
}
64+
}
65+
66+
/**
67+
* Oscillates a [RecyclerView]'s children based on the horizontal scroll velocity.
68+
*/
69+
private const val MAX_OSCILLATION_ANGLE = 6f // ±6º
70+
class OscillatingScrollListener(
71+
@Px private val scrollDistance: Int
72+
) : RecyclerView.OnScrollListener() {
73+
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
74+
// Calculate a rotation to set from the horizontal scroll
75+
val rotation =
76+
(dx.coerceIn(-scrollDistance, scrollDistance) / scrollDistance) * MAX_OSCILLATION_ANGLE
77+
recyclerView.forEach {
78+
it.spring(SpringAnimation.ROTATION).animateToFinalPosition(rotation)
79+
}
80+
}
81+
82+
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
83+
if (newState != SCROLL_STATE_DRAGGING) {
84+
recyclerView.forEach {
85+
it.spring(SpringAnimation.ROTATION).animateToFinalPosition(0f)
86+
}
87+
}
88+
}
89+
}

0 commit comments

Comments
 (0)