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

Commit 8357fe9

Browse files
committed
Add NestedMenuCandidate concept item
1 parent 552b63f commit 8357fe9

File tree

13 files changed

+191
-41
lines changed

13 files changed

+191
-41
lines changed

components/browser/menu/src/main/java/mozilla/components/browser/menu/ext/BrowserMenuItem.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
package mozilla.components.browser.menu.ext
66

7+
import android.content.Context
78
import mozilla.components.browser.menu.BrowserMenuHighlight
89
import mozilla.components.browser.menu.BrowserMenuItem
910
import mozilla.components.browser.menu.HighlightableMenuItem
@@ -26,3 +27,9 @@ fun List<BrowserMenuItem>.getHighlight() = asSequence()
2627
is BrowserMenuHighlight.ClassicHighlight -> 0
2728
}
2829
}
30+
31+
/**
32+
* Converts the menu items into a menu candidate list.
33+
*/
34+
fun List<BrowserMenuItem>.asCandidateList(context: Context) =
35+
mapNotNull { it.asCandidate(context) }

components/browser/menu/src/main/java/mozilla/components/browser/menu/item/BackPressMenuItem.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44

55
package mozilla.components.browser.menu.item
66

7+
import android.content.Context
78
import android.view.View
89
import androidx.annotation.ColorRes
910
import androidx.annotation.DrawableRes
1011
import mozilla.components.browser.menu.BrowserMenu
12+
import mozilla.components.concept.menu.candidate.NestedMenuCandidate
13+
import mozilla.components.concept.menu.candidate.TextMenuCandidate
1114

1215
/**
1316
* A back press menu item for a nested sub menu entry.
@@ -42,4 +45,16 @@ class BackPressMenuItem(
4245
fun setListener(onClickListener: () -> Unit) {
4346
backPressListener = onClickListener
4447
}
48+
49+
override fun asCandidate(context: Context): NestedMenuCandidate {
50+
val parentCandidate = super.asCandidate(context) as TextMenuCandidate
51+
return NestedMenuCandidate(
52+
id = hashCode(),
53+
text = parentCandidate.text,
54+
start = parentCandidate.start,
55+
subMenuItems = null,
56+
textStyle = parentCandidate.textStyle,
57+
containerStyle = parentCandidate.containerStyle
58+
)
59+
}
4560
}

components/browser/menu/src/main/java/mozilla/components/browser/menu/item/BrowserMenuHighlightableItem.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ class BrowserMenuHighlightableItem(
146146
}
147147

148148
override fun asCandidate(context: Context): TextMenuCandidate {
149-
val base = super.asCandidate(context)
149+
val base = super.asCandidate(context) as TextMenuCandidate
150150
if (!isHighlighted()) return base
151151

152152
@Suppress("Deprecation")

components/browser/menu/src/main/java/mozilla/components/browser/menu/item/BrowserMenuImageText.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import mozilla.components.browser.menu.BrowserMenuItem
1818
import mozilla.components.browser.menu.R
1919
import mozilla.components.concept.menu.candidate.ContainerStyle
2020
import mozilla.components.concept.menu.candidate.DrawableMenuIcon
21+
import mozilla.components.concept.menu.candidate.MenuCandidate
2122
import mozilla.components.concept.menu.candidate.TextMenuCandidate
2223
import mozilla.components.concept.menu.candidate.TextStyle
2324

@@ -84,7 +85,7 @@ open class BrowserMenuImageText(
8485
}
8586
}
8687

87-
override fun asCandidate(context: Context) = TextMenuCandidate(
88+
override fun asCandidate(context: Context): MenuCandidate = TextMenuCandidate(
8889
label,
8990
start = DrawableMenuIcon(
9091
context,

components/browser/menu/src/main/java/mozilla/components/browser/menu/item/ParentBrowserMenuItem.kt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@ import androidx.appcompat.widget.AppCompatImageView
1313
import androidx.core.content.ContextCompat
1414
import mozilla.components.browser.menu.BrowserMenu
1515
import mozilla.components.browser.menu.R
16+
import mozilla.components.browser.menu.ext.asCandidateList
1617
import mozilla.components.concept.menu.candidate.ContainerStyle
1718
import mozilla.components.concept.menu.candidate.DrawableMenuIcon
18-
import mozilla.components.concept.menu.candidate.TextMenuCandidate
19+
import mozilla.components.concept.menu.candidate.NestedMenuCandidate
1920
import mozilla.components.concept.menu.candidate.TextStyle
2021

2122
/**
@@ -81,13 +82,15 @@ class ParentBrowserMenuItem(
8182
}
8283
}
8384

84-
override fun asCandidate(context: Context) = TextMenuCandidate(
85-
label,
85+
override fun asCandidate(context: Context) = NestedMenuCandidate(
86+
id = hashCode(),
87+
text = label,
8688
start = DrawableMenuIcon(
8789
context,
8890
resource = imageResource,
8991
tint = if (iconTintColorResource == NO_ID) null else ContextCompat.getColor(context, iconTintColorResource)
9092
),
93+
subMenuItems = subMenu.adapter.visibleItems.asCandidateList(context),
9194
textStyle = TextStyle(
9295
color = if (textColorResource == NO_ID) null else ContextCompat.getColor(context, textColorResource)
9396
),

components/browser/menu/src/test/java/mozilla/components/browser/menu/item/BrowserMenuImageTextTest.kt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,9 @@ class BrowserMenuImageTextTest {
9595
"label",
9696
android.R.drawable.ic_menu_report_image,
9797
listener = listener
98-
).asCandidate(context).run {
99-
copy(start = (start as? DrawableMenuIcon)?.copy(drawable = null))
98+
).asCandidate(context).let {
99+
val text = it as TextMenuCandidate
100+
text.copy(start = (text.start as? DrawableMenuIcon)?.copy(drawable = null))
100101
}
101102
)
102103

@@ -114,8 +115,9 @@ class BrowserMenuImageTextTest {
114115
android.R.drawable.ic_menu_report_image,
115116
android.R.color.black,
116117
listener = listener
117-
).asCandidate(context).run {
118-
copy(start = (start as? DrawableMenuIcon)?.copy(drawable = null))
118+
).asCandidate(context).let {
119+
val text = it as TextMenuCandidate
120+
text.copy(start = (text.start as? DrawableMenuIcon)?.copy(drawable = null))
119121
}
120122
)
121123
}

components/browser/menu/src/test/java/mozilla/components/browser/menu/item/ParentBrowserMenuItemTest.kt

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,13 @@ package mozilla.components.browser.menu.item
77
import android.view.LayoutInflater
88
import android.widget.TextView
99
import androidx.appcompat.widget.AppCompatImageView
10-
import androidx.core.content.ContextCompat
1110
import androidx.test.ext.junit.runners.AndroidJUnit4
1211
import mozilla.components.browser.menu.BrowserMenu
1312
import mozilla.components.browser.menu.BrowserMenuAdapter
1413
import mozilla.components.browser.menu.R
14+
import mozilla.components.concept.menu.candidate.DecorativeTextMenuCandidate
1515
import mozilla.components.concept.menu.candidate.DrawableMenuIcon
16-
import mozilla.components.concept.menu.candidate.TextMenuCandidate
17-
import mozilla.components.support.test.mock
16+
import mozilla.components.concept.menu.candidate.NestedMenuCandidate
1817
import mozilla.components.support.test.robolectric.testContext
1918
import org.junit.Assert.assertEquals
2019
import org.junit.Assert.assertFalse
@@ -108,36 +107,41 @@ class ParentBrowserMenuItemTest {
108107

109108
@Test
110109
fun `menu item image text item can be converted to candidate`() {
111-
assertEquals(
112-
TextMenuCandidate(
113-
"label",
114-
start = DrawableMenuIcon(null)
115-
),
116-
ParentBrowserMenuItem(
117-
"label",
118-
android.R.drawable.ic_menu_report_image,
119-
subMenu = mock()
120-
).asCandidate(testContext).run {
121-
copy(start = (start as? DrawableMenuIcon)?.copy(drawable = null))
122-
}
110+
val backPressMenuItem = BackPressMenuItem(
111+
label = "back",
112+
imageResource = R.drawable.mozac_ic_back
113+
)
114+
val subMenuItem = SimpleBrowserMenuItem("test")
115+
val subMenuAdapter = BrowserMenuAdapter(testContext, listOf(backPressMenuItem, subMenuItem))
116+
val subMenu = BrowserMenu(subMenuAdapter)
117+
val menuItem = ParentBrowserMenuItem(
118+
"label",
119+
android.R.drawable.ic_menu_report_image,
120+
subMenu = subMenu
123121
)
124122

123+
val candidate = menuItem.asCandidate(testContext)
124+
125+
assertEquals(menuItem.hashCode(), candidate.id)
126+
assertEquals("label", candidate.text)
127+
assertEquals(2, candidate.subMenuItems!!.size)
128+
129+
val backCandidate = candidate.subMenuItems!![0] as NestedMenuCandidate
130+
val testCandidate = candidate.subMenuItems!![1] as DecorativeTextMenuCandidate
125131
assertEquals(
126-
TextMenuCandidate(
127-
text = "label",
128-
start = DrawableMenuIcon(
129-
drawable = null,
130-
tint = ContextCompat.getColor(testContext, android.R.color.black)
131-
)
132+
NestedMenuCandidate(
133+
id = backPressMenuItem.hashCode(),
134+
text = "back",
135+
start = DrawableMenuIcon(null),
136+
subMenuItems = null
132137
),
133-
ParentBrowserMenuItem(
134-
"label",
135-
android.R.drawable.ic_menu_report_image,
136-
android.R.color.black,
137-
subMenu = mock()
138-
).asCandidate(testContext).run {
138+
backCandidate.run {
139139
copy(start = (start as? DrawableMenuIcon)?.copy(drawable = null))
140140
}
141141
)
142+
assertEquals(
143+
DecorativeTextMenuCandidate("test"),
144+
testCandidate
145+
)
142146
}
143147
}

components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/LastItemViewHolder.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ internal abstract class LastItemViewHolder<T>(
1515
itemView: View
1616
) : RecyclerView.ViewHolder(itemView) {
1717

18-
private var lastCandidate: T? = null
18+
protected var lastCandidate: T? = null
1919

2020
/**
2121
* Updates the held view to reflect changes in the menu option.

components/browser/menu2/src/main/java/mozilla/components/browser/menu2/adapter/MenuCandidateListAdapter.kt

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,30 @@ import mozilla.components.concept.menu.candidate.CompoundMenuCandidate
1313
import mozilla.components.concept.menu.candidate.DecorativeTextMenuCandidate
1414
import mozilla.components.concept.menu.candidate.DividerMenuCandidate
1515
import mozilla.components.concept.menu.candidate.MenuCandidate
16+
import mozilla.components.concept.menu.candidate.NestedMenuCandidate
1617
import mozilla.components.concept.menu.candidate.RowMenuCandidate
1718
import mozilla.components.concept.menu.candidate.TextMenuCandidate
1819

1920
internal class MenuCandidateListAdapter(
2021
private val inflater: LayoutInflater,
21-
private val dismiss: () -> Unit
22+
private val dismiss: () -> Unit,
23+
private val reopenMenu: (NestedMenuCandidate) -> Unit
2224
) : ListAdapter<MenuCandidate, MenuCandidateViewHolder<out MenuCandidate>>(MenuCandidateDiffer) {
2325

2426
@LayoutRes
2527
override fun getItemViewType(position: Int) = when (val item = getItem(position)) {
2628
is TextMenuCandidate -> TextMenuCandidateViewHolder.layoutResource
2729
is DecorativeTextMenuCandidate -> DecorativeTextMenuCandidateViewHolder.layoutResource
2830
is CompoundMenuCandidate -> CompoundMenuCandidateViewHolder.getLayoutResource(item)
31+
is NestedMenuCandidate -> NestedMenuCandidateViewHolder.layoutResource
2932
is RowMenuCandidate -> RowMenuCandidateViewHolder.layoutResource
3033
is DividerMenuCandidate -> DividerMenuCandidateViewHolder.layoutResource
3134
}
3235

33-
override fun onCreateViewHolder(parent: ViewGroup, @LayoutRes viewType: Int): MenuCandidateViewHolder<*> {
36+
override fun onCreateViewHolder(
37+
parent: ViewGroup,
38+
@LayoutRes viewType: Int
39+
): MenuCandidateViewHolder<out MenuCandidate> {
3440
val view = inflater.inflate(viewType, parent, false)
3541
return when (viewType) {
3642
TextMenuCandidateViewHolder.layoutResource ->
@@ -41,6 +47,8 @@ internal class MenuCandidateListAdapter(
4147
CompoundCheckboxMenuCandidateViewHolder(view, inflater, dismiss)
4248
CompoundSwitchMenuCandidateViewHolder.layoutResource ->
4349
CompoundSwitchMenuCandidateViewHolder(view, inflater, dismiss)
50+
NestedMenuCandidateViewHolder.layoutResource ->
51+
NestedMenuCandidateViewHolder(view, inflater, dismiss, reopenMenu)
4452
RowMenuCandidateViewHolder.layoutResource ->
4553
RowMenuCandidateViewHolder(view, inflater, dismiss)
4654
DividerMenuCandidateViewHolder.layoutResource ->
@@ -49,12 +57,13 @@ internal class MenuCandidateListAdapter(
4957
}
5058
}
5159

52-
override fun onBindViewHolder(holder: MenuCandidateViewHolder<*>, position: Int) {
60+
override fun onBindViewHolder(holder: MenuCandidateViewHolder<out MenuCandidate>, position: Int) {
5361
val item = getItem(position)
5462
when (holder) {
5563
is TextMenuCandidateViewHolder -> holder.bind(item as TextMenuCandidate)
5664
is DecorativeTextMenuCandidateViewHolder -> holder.bind(item as DecorativeTextMenuCandidate)
5765
is CompoundMenuCandidateViewHolder -> holder.bind(item as CompoundMenuCandidate)
66+
is NestedMenuCandidateViewHolder -> holder.bind(item as NestedMenuCandidate)
5867
is RowMenuCandidateViewHolder -> holder.bind(item as RowMenuCandidate)
5968
is DividerMenuCandidateViewHolder -> holder.bind(item as DividerMenuCandidate)
6069
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
5+
package mozilla.components.browser.menu2.adapter
6+
7+
import android.view.LayoutInflater
8+
import android.view.View
9+
import android.widget.TextView
10+
import androidx.annotation.LayoutRes
11+
import androidx.constraintlayout.widget.ConstraintLayout
12+
import mozilla.components.browser.menu2.R
13+
import mozilla.components.browser.menu2.adapter.icons.MenuIconAdapter
14+
import mozilla.components.browser.menu2.ext.applyBackgroundEffect
15+
import mozilla.components.browser.menu2.ext.applyStyle
16+
import mozilla.components.concept.menu.Side
17+
import mozilla.components.concept.menu.candidate.NestedMenuCandidate
18+
19+
internal class NestedMenuCandidateViewHolder(
20+
itemView: View,
21+
inflater: LayoutInflater,
22+
dismiss: () -> Unit,
23+
private val reopenMenu: (NestedMenuCandidate) -> Unit
24+
) : MenuCandidateViewHolder<NestedMenuCandidate>(itemView, inflater), View.OnClickListener {
25+
26+
private val layout = itemView as ConstraintLayout
27+
private val textView: TextView get() = itemView.findViewById(R.id.label)
28+
private val startIcon = MenuIconAdapter(layout, inflater, Side.START, dismiss)
29+
private val endIcon = MenuIconAdapter(layout, inflater, Side.END, dismiss)
30+
31+
init {
32+
itemView.setOnClickListener(this)
33+
}
34+
35+
override fun bind(newCandidate: NestedMenuCandidate, oldCandidate: NestedMenuCandidate?) {
36+
super.bind(newCandidate, oldCandidate)
37+
38+
textView.text = newCandidate.text
39+
textView.applyStyle(newCandidate.textStyle, oldCandidate?.textStyle)
40+
itemView.applyBackgroundEffect(newCandidate.effect, oldCandidate?.effect)
41+
startIcon.bind(newCandidate.start, oldCandidate?.start)
42+
endIcon.bind(newCandidate.end, oldCandidate?.end)
43+
}
44+
45+
override fun onClick(v: View?) {
46+
lastCandidate?.let { reopenMenu(it) }
47+
}
48+
49+
companion object {
50+
@LayoutRes
51+
val layoutResource = R.layout.mozac_browser_menu2_candidate_nested
52+
}
53+
}

0 commit comments

Comments
 (0)