Skip to content

Commit 40a2dd0

Browse files
committed
Added "how to use?" tutorial in settings menu
1 parent e976fca commit 40a2dd0

20 files changed

Lines changed: 538 additions & 0 deletions

app/src/main/java/com/wstxda/clippy/activity/BaseActivity.kt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package com.wstxda.clippy.activity
33
import android.graphics.Color
44
import android.os.Build
55
import android.os.Bundle
6+
import android.view.Menu
7+
import android.view.MenuItem
68
import android.view.View
79
import android.widget.Toast
810
import androidx.activity.SystemBarStyle
@@ -46,6 +48,20 @@ abstract class BaseActivity : AppCompatActivity() {
4648
}
4749
}
4850

51+
protected open fun getMenuResId(): Int? = null
52+
53+
override fun onCreateOptionsMenu(menu: Menu): Boolean {
54+
getMenuResId()?.let { menuInflater.inflate(it, menu) }
55+
return super.onCreateOptionsMenu(menu)
56+
}
57+
58+
protected open fun onMenuItemSelected(item: MenuItem): Boolean = false
59+
60+
override fun onOptionsItemSelected(item: MenuItem): Boolean {
61+
return if (onMenuItemSelected(item)) true
62+
else super.onOptionsItemSelected(item)
63+
}
64+
4965
protected fun showToast(message: String) {
5066
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
5167
}

app/src/main/java/com/wstxda/clippy/activity/SettingsActivity.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package com.wstxda.clippy.activity
22

33
import android.os.Bundle
4+
import android.view.MenuItem
45
import com.wstxda.clippy.R
56
import com.wstxda.clippy.databinding.ActivitySettingsBinding
7+
import com.wstxda.clippy.ui.component.TutorialBottomSheet
68

79
class SettingsActivity : BaseActivity() {
810

@@ -16,4 +18,17 @@ class SettingsActivity : BaseActivity() {
1618
setupToolbar(binding.toolbar, showBackButton = false)
1719
binding.collapsingToolbar.title = getString(R.string.app_name)
1820
}
21+
22+
override fun getMenuResId(): Int = R.menu.main_menu
23+
24+
override fun onMenuItemSelected(item: MenuItem): Boolean {
25+
return when (item.itemId) {
26+
R.id.action_show_tutorial -> {
27+
TutorialBottomSheet.show(supportFragmentManager)
28+
true
29+
}
30+
31+
else -> false
32+
}
33+
}
1934
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.wstxda.clippy.data
2+
3+
data class TutorialItem(
4+
5+
val iconRes: Int,
6+
val imageRes: Int,
7+
val titleRes: Int,
8+
val summaryRes: Int,
9+
)
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package com.wstxda.clippy.fragment
2+
3+
import android.content.Context
4+
import android.os.Bundle
5+
import android.view.LayoutInflater
6+
import android.view.View
7+
import android.view.ViewGroup
8+
import androidx.fragment.app.Fragment
9+
import com.wstxda.clippy.databinding.FragmentTutorialBinding
10+
import com.wstxda.clippy.ui.adapter.TutorialItemViewBuilder
11+
import com.wstxda.clippy.ui.utils.TutorialItemList
12+
13+
class TutorialFragment : Fragment() {
14+
15+
interface ScrollListener {
16+
fun onScrollChanged(canScrollUp: Boolean, canScrollDown: Boolean)
17+
}
18+
19+
private var _binding: FragmentTutorialBinding? = null
20+
private val binding get() = _binding!!
21+
private var scrollListener: ScrollListener? = null
22+
23+
override fun onAttach(context: Context) {
24+
super.onAttach(context)
25+
if (parentFragment is ScrollListener) {
26+
scrollListener = parentFragment as ScrollListener
27+
}
28+
}
29+
30+
override fun onCreateView(
31+
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?,
32+
): View {
33+
_binding = FragmentTutorialBinding.inflate(inflater, container, false)
34+
return binding.root
35+
}
36+
37+
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
38+
super.onViewCreated(view, savedInstanceState)
39+
populateTutorials()
40+
41+
binding.tutorialScrollView.setOnScrollChangeListener { v, _, _, _, _ ->
42+
scrollListener?.onScrollChanged(
43+
v.canScrollVertically(-1), v.canScrollVertically(1)
44+
)
45+
}
46+
47+
binding.tutorialScrollView.post {
48+
scrollListener?.onScrollChanged(
49+
binding.tutorialScrollView.canScrollVertically(-1),
50+
binding.tutorialScrollView.canScrollVertically(1)
51+
)
52+
}
53+
}
54+
55+
private fun populateTutorials() {
56+
val items = TutorialItemList.getTutorialItems()
57+
items.forEach { item ->
58+
val view = TutorialItemViewBuilder.build(requireContext(), item)
59+
binding.tutorialContainer.addView(view)
60+
}
61+
}
62+
63+
override fun onDestroyView() {
64+
super.onDestroyView()
65+
binding.tutorialContainer.removeAllViews()
66+
_binding = null
67+
}
68+
69+
override fun onDetach() {
70+
super.onDetach()
71+
scrollListener = null
72+
}
73+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.wstxda.clippy.ui.adapter
2+
3+
import android.content.Context
4+
import android.view.LayoutInflater
5+
import android.view.View
6+
import com.wstxda.clippy.data.TutorialItem
7+
import com.wstxda.clippy.databinding.ListItemTutorialBinding
8+
9+
object TutorialItemViewBuilder {
10+
11+
fun build(context: Context, item: TutorialItem): View {
12+
val binding = ListItemTutorialBinding.inflate(LayoutInflater.from(context))
13+
binding.tutorialImage.setImageResource(item.imageRes)
14+
binding.tutorialTitle.setText(item.titleRes)
15+
binding.tutorialTitle.setCompoundDrawablesRelativeWithIntrinsicBounds(item.iconRes, 0, 0, 0)
16+
binding.tutorialSummary.setText(item.summaryRes)
17+
return binding.root
18+
}
19+
}

app/src/main/java/com/wstxda/clippy/ui/component/BaseBottomSheet.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ abstract class BaseBottomSheet<VB : ViewBinding> : BottomSheetDialogFragment() {
3737
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
3838
super.onViewCreated(view, savedInstanceState)
3939
titleTextView.setText(titleResId)
40+
setupContentFragment(savedInstanceState)
4041
setupViews(savedInstanceState)
4142
setupScrollListener()
4243
}
@@ -46,6 +47,7 @@ abstract class BaseBottomSheet<VB : ViewBinding> : BottomSheetDialogFragment() {
4647
_binding = null
4748
}
4849

50+
protected open fun setupContentFragment(savedInstanceState: Bundle?) {}
4951
protected open fun setupViews(savedInstanceState: Bundle?) {}
5052
protected open fun setupScrollListener() {}
5153

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.wstxda.clippy.ui.component
2+
3+
import android.os.Bundle
4+
import android.view.LayoutInflater
5+
import android.view.View
6+
import android.view.ViewGroup
7+
import android.widget.TextView
8+
import androidx.fragment.app.FragmentManager
9+
import com.wstxda.clippy.R
10+
import com.wstxda.clippy.databinding.BottomSheetTutorialBinding
11+
import com.wstxda.clippy.fragment.TutorialFragment
12+
import com.wstxda.clippy.utils.Constants
13+
14+
class TutorialBottomSheet : BaseBottomSheet<BottomSheetTutorialBinding>(),
15+
TutorialFragment.ScrollListener {
16+
17+
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?) =
18+
BottomSheetTutorialBinding.inflate(inflater, container, false)
19+
20+
override val topDivider: View get() = binding.dividerTop
21+
override val bottomDivider: View get() = binding.dividerBottom
22+
override val titleTextView: TextView get() = binding.bottomSheetTitle
23+
override val titleResId: Int get() = R.string.tutorial_title
24+
25+
override fun setupContentFragment(savedInstanceState: Bundle?) {
26+
if (savedInstanceState == null) {
27+
childFragmentManager.beginTransaction()
28+
.replace(binding.tutorialFragmentContainer.id, TutorialFragment()).commit()
29+
}
30+
}
31+
32+
override fun onScrollChanged(canScrollUp: Boolean, canScrollDown: Boolean) {
33+
updateDividerVisibility(canScrollUp, canScrollDown)
34+
}
35+
36+
companion object {
37+
fun show(fragmentManager: FragmentManager) {
38+
TutorialBottomSheet().show(fragmentManager, Constants.TUTORIAL_BOTTOM_SHEET)
39+
}
40+
}
41+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.wstxda.clippy.ui.utils
2+
3+
import com.wstxda.clippy.R
4+
import com.wstxda.clippy.data.TutorialItem
5+
6+
object TutorialItemList {
7+
fun getTutorialItems() = listOf(
8+
9+
// Share menu tutorial
10+
TutorialItem(
11+
iconRes = R.drawable.ic_share,
12+
imageRes = R.drawable.tutorial_share_card,
13+
titleRes = R.string.tutorial_share_menu,
14+
summaryRes = R.string.tutorial_share_menu_summary
15+
16+
// Select text tutorial
17+
), TutorialItem(
18+
iconRes = R.drawable.ic_text_select,
19+
imageRes = R.drawable.tutorial_text_share_card,
20+
titleRes = R.string.tutorial_select_text_menu,
21+
summaryRes = R.string.tutorial_select_text_summary
22+
)
23+
)
24+
}

app/src/main/java/com/wstxda/clippy/utils/Constants.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ object Constants {
2727

2828
// UI/ViewModel
2929
const val LINK_CLEANER_BOTTOM_SHEET = "LinkCleanerBottomSheet"
30+
const val TUTORIAL_BOTTOM_SHEET = "TutorialBottomSheet"
3031
const val LINK_CLEANER_VIEW_MODEL = "LinkCleanerViewModel"
3132
// Modules
3233
const val BUILTIN_RULES_RESOLVER = "BuiltinRulesResolver"
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
2+
android:width="24dp"
3+
android:height="24dp"
4+
android:autoMirrored="true"
5+
android:tint="?attr/colorControlNormal"
6+
android:viewportWidth="960"
7+
android:viewportHeight="960">
8+
<path
9+
android:fillColor="@android:color/white"
10+
android:pathData="M513.5,705.5Q528,691 528,670Q528,649 513.5,634.5Q499,620 478,620Q457,620 442.5,634.5Q428,649 428,670Q428,691 442.5,705.5Q457,720 478,720Q499,720 513.5,705.5ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480ZM484,308Q509,308 527.5,324Q546,340 546,364Q546,386 532.5,403Q519,420 502,435Q479,455 461.5,479Q444,503 444,533Q444,547 454.5,556.5Q465,566 479,566Q494,566 504.5,556Q515,546 518,531Q522,510 536,493.5Q550,477 566,462Q589,440 605.5,414Q622,388 622,356Q622,305 580.5,272.5Q539,240 484,240Q446,240 411.5,256Q377,272 359,305Q352,317 354.5,330.5Q357,344 368,351Q382,359 397,356Q412,353 422,339Q433,324 449.5,316Q466,308 484,308Z" />
11+
</vector>

0 commit comments

Comments
 (0)