Skip to content

Commit b4adf1e

Browse files
MozLandoAmejia481
andcommitted
6406: Add api for customizing the ui of the AddonPermissionsAdapter and AddonsManagerAdapter r=psymoon a=Amejia481 Relate Fenix issue mozilla-mobile/fenix#8520 Co-authored-by: Arturo Mejia <[email protected]>
2 parents 5131ec9 + d31130b commit b4adf1e

File tree

7 files changed

+148
-13
lines changed

7 files changed

+148
-13
lines changed

components/feature/addons/src/main/java/mozilla/components/feature/addons/ui/AddonPermissionsAdapter.kt

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,20 @@ import android.view.LayoutInflater
88
import android.view.View
99
import android.view.ViewGroup
1010
import android.widget.TextView
11+
import androidx.annotation.ColorRes
12+
import androidx.core.content.ContextCompat
1113
import androidx.recyclerview.widget.RecyclerView
1214
import mozilla.components.feature.addons.R
1315

1416
/**
1517
* An adapter for displaying the permissions of an add-on.
1618
*
17-
* @property permissions The list of [Addon] permissions to display.
19+
* @property permissions The list of [mozilla.components.feature.addons.Addon] permissions to display.
20+
* @property style Indicates how permission items should look like.
1821
*/
1922
class AddonPermissionsAdapter(
20-
private val permissions: List<String>
23+
private val permissions: List<String>,
24+
private val style: Style? = null
2125
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
2226
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PermissionViewHolder {
2327
val context = parent.context
@@ -35,7 +39,10 @@ class AddonPermissionsAdapter(
3539
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
3640
holder as PermissionViewHolder
3741
val permission = permissions[position]
38-
holder.textView.text = permission
42+
with(holder.textView) {
43+
text = permission
44+
style?.maybeSetItemTextColor(this)
45+
}
3946
}
4047

4148
/**
@@ -45,4 +52,16 @@ class AddonPermissionsAdapter(
4552
val view: View,
4653
val textView: TextView
4754
) : RecyclerView.ViewHolder(view)
55+
56+
/**
57+
* Allows to customize how permission items should look like.
58+
*/
59+
data class Style(@ColorRes val itemsTextColor: Int? = null) {
60+
internal fun maybeSetItemTextColor(textView: TextView) {
61+
itemsTextColor?.let {
62+
val color = ContextCompat.getColor(textView.context, it)
63+
textView.setTextColor(color)
64+
}
65+
}
66+
}
4867
}

components/feature/addons/src/main/java/mozilla/components/feature/addons/ui/AddonsManagerAdapter.kt

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import android.view.ViewGroup
1313
import android.widget.ImageView
1414
import android.widget.RatingBar
1515
import android.widget.TextView
16+
import androidx.annotation.ColorRes
1617
import androidx.annotation.StringRes
1718
import androidx.annotation.VisibleForTesting
1819
import androidx.core.content.ContextCompat
@@ -45,12 +46,14 @@ private const val VIEW_HOLDER_TYPE_ADDON = 2
4546
* @property addonCollectionProvider Provider of AMO collection API.
4647
* @property addonsManagerDelegate Delegate that will provides method for handling the add-on items.
4748
* @property addons The list of add-on based on the AMO store.
49+
* @property style Indicates how items should look like.
4850
*/
4951
@Suppress("TooManyFunctions", "LargeClass")
5052
class AddonsManagerAdapter(
5153
private val addonCollectionProvider: AddonCollectionProvider,
5254
private val addonsManagerDelegate: AddonsManagerAdapterDelegate,
53-
addons: List<Addon>
55+
addons: List<Addon>,
56+
private val style: Style? = null
5457
) : RecyclerView.Adapter<CustomViewHolder>() {
5558
private val scope = CoroutineScope(Dispatchers.IO)
5659
private val items: List<Any>
@@ -75,7 +78,6 @@ class AddonsManagerAdapter(
7578
val inflater = LayoutInflater.from(context)
7679
val view = inflater.inflate(R.layout.mozac_feature_addons_section_item, parent, false)
7780
val titleView = view.findViewById<TextView>(R.id.title)
78-
7981
return SectionViewHolder(view, titleView)
8082
}
8183

@@ -140,8 +142,10 @@ class AddonsManagerAdapter(
140142
}
141143
}
142144

143-
private fun bindSection(holder: SectionViewHolder, section: Section) {
145+
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
146+
internal fun bindSection(holder: SectionViewHolder, section: Section) {
144147
holder.titleView.setText(section.title)
148+
style?.maybeSetStatusTextColor(holder.titleView)
145149
}
146150

147151
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
@@ -210,6 +214,8 @@ class AddonsManagerAdapter(
210214
}
211215

212216
fetchIcon(addon, holder.iconView)
217+
style?.maybeSetAddonNameTextColor(holder.titleView)
218+
style?.maybeSetAddonSummaryTextColor(holder.summaryView)
213219
}
214220

215221
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
@@ -278,10 +284,43 @@ class AddonsManagerAdapter(
278284
}
279285

280286
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
281-
internal inner class Section(@StringRes val title: Int)
287+
internal data class Section(@StringRes val title: Int)
282288

283289
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
284-
internal inner class NotYetSupportedSection(@StringRes val title: Int)
290+
internal data class NotYetSupportedSection(@StringRes val title: Int)
291+
292+
/**
293+
* Allows to customize how items should look like.
294+
*/
295+
data class Style(
296+
@ColorRes
297+
val sectionsTextColor: Int? = null,
298+
@ColorRes
299+
val addonNameTextColor: Int? = null,
300+
@ColorRes
301+
val addonSummaryTextColor: Int? = null
302+
) {
303+
internal fun maybeSetStatusTextColor(textView: TextView) {
304+
sectionsTextColor?.let {
305+
val color = ContextCompat.getColor(textView.context, it)
306+
textView.setTextColor(color)
307+
}
308+
}
309+
310+
internal fun maybeSetAddonNameTextColor(textView: TextView) {
311+
addonNameTextColor?.let {
312+
val color = ContextCompat.getColor(textView.context, it)
313+
textView.setTextColor(color)
314+
}
315+
}
316+
317+
internal fun maybeSetAddonSummaryTextColor(textView: TextView) {
318+
addonSummaryTextColor?.let {
319+
val color = ContextCompat.getColor(textView.context, it)
320+
textView.setTextColor(color)
321+
}
322+
}
323+
}
285324
}
286325

287326
private fun Addon.inUnsupportedSection() = isInstalled() && !isSupported()

components/feature/addons/src/main/res/layout/mozac_feature_addons_item.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,17 +53,17 @@
5353
android:id="@+id/add_on_name"
5454
android:layout_width="wrap_content"
5555
android:layout_height="wrap_content"
56-
android:layout_marginBottom="6dp"
56+
android:layout_marginBottom="2dp"
5757
android:ellipsize="end"
5858
android:maxLines="1"
59-
android:textStyle="bold"
59+
android:textSize="16sp"
6060
tools:text="uBlock Origin" />
6161

6262
<TextView
6363
android:id="@+id/add_on_description"
6464
android:layout_width="match_parent"
6565
android:layout_height="wrap_content"
66-
android:textSize="12sp"
66+
android:textSize="14sp"
6767
tools:text="An efficient blocker: easy on memory and CPU footprint, and yet can load and enforce thousands more filters than other popular blockers out there." />
6868

6969
<LinearLayout

components/feature/addons/src/main/res/layout/mozac_feature_addons_permission_item.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,13 @@
1919
android:layout_height="wrap_content"
2020
android:paddingTop="16dp"
2121
android:paddingBottom="16dp"
22+
android:textSize="16sp"
2223
android:textColor="?android:attr/textColorPrimary"
2324
tools:text="Access your data for all websites" />
2425

2526
<View
2627
android:layout_width="match_parent"
2728
android:layout_height="1dp"
28-
android:background="@color/photonGrey40"
29+
android:background="?android:attr/listDivider"
2930
android:importantForAccessibility="no" />
3031
</LinearLayout>

components/feature/addons/src/test/java/mozilla/components/feature/addons/ui/AddonsManagerAdapterTest.kt

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package mozilla.components.feature.addons.amo.mozilla.components.feature.addons.
77
import android.view.View
88
import android.widget.ImageView
99
import android.widget.TextView
10+
import androidx.core.content.ContextCompat
1011
import androidx.test.ext.junit.runners.AndroidJUnit4
1112
import kotlinx.coroutines.ExperimentalCoroutinesApi
1213
import kotlinx.coroutines.runBlocking
@@ -146,13 +147,24 @@ class AddonsManagerAdapterTest {
146147
translatableDescription = mapOf("en-US" to "description", "de" to "Beschreibung", "es" to "descripción"),
147148
translatableSummary = mapOf("en-US" to "summary", "de" to "Kurzfassung", "es" to "resumen")
148149
)
149-
val adapter = AddonsManagerAdapter(mock(), addonsManagerAdapterDelegate, emptyList())
150+
151+
whenever(titleView.context).thenReturn(testContext)
152+
whenever(summaryView.context).thenReturn(testContext)
153+
154+
val style = AddonsManagerAdapter.Style(
155+
sectionsTextColor = android.R.color.black,
156+
addonNameTextColor = android.R.color.transparent,
157+
addonSummaryTextColor = android.R.color.white
158+
)
159+
val adapter = AddonsManagerAdapter(mock(), addonsManagerAdapterDelegate, emptyList(), style)
150160

151161
adapter.bindAddon(addonViewHolder, addon)
152162

153163
verify(ratingAccessibleView).setText("4.50/5")
154164
verify(titleView).setText("name")
165+
verify(titleView).setTextColor(ContextCompat.getColor(testContext, style.addonNameTextColor!!))
155166
verify(summaryView).setText("summary")
167+
verify(summaryView).setTextColor(ContextCompat.getColor(testContext, style.addonSummaryTextColor!!))
156168
assertNotNull(addonViewHolder.itemView.tag)
157169

158170
addonViewHolder.itemView.performClick()
@@ -161,6 +173,24 @@ class AddonsManagerAdapterTest {
161173
verify(addonsManagerAdapterDelegate).onInstallAddonButtonClicked(addon)
162174
}
163175

176+
@Test
177+
fun `bind section`() {
178+
val titleView: TextView = mock()
179+
val addonViewHolder = CustomViewHolder.SectionViewHolder(View(testContext), titleView)
180+
181+
whenever(titleView.context).thenReturn(testContext)
182+
183+
val style = AddonsManagerAdapter.Style(
184+
sectionsTextColor = android.R.color.black
185+
)
186+
val adapter = AddonsManagerAdapter(mock(), mock(), emptyList(), style)
187+
188+
adapter.bindSection(addonViewHolder, Section(R.string.mozac_feature_addons_disabled_section))
189+
190+
verify(titleView).setText(R.string.mozac_feature_addons_disabled_section)
191+
verify(titleView).setTextColor(ContextCompat.getColor(testContext, style.sectionsTextColor!!))
192+
}
193+
164194
@Test
165195
fun `bind add-on with no available translatable name`() {
166196
Locale.setDefault(Locale.ENGLISH)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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.feature.addons.ui
6+
7+
import android.view.View
8+
import android.widget.TextView
9+
import androidx.core.content.ContextCompat
10+
import androidx.test.ext.junit.runners.AndroidJUnit4
11+
import kotlinx.coroutines.ExperimentalCoroutinesApi
12+
import mozilla.components.feature.addons.R
13+
import mozilla.components.feature.addons.ui.AddonPermissionsAdapter.PermissionViewHolder
14+
import mozilla.components.feature.addons.ui.AddonPermissionsAdapter.Style
15+
import mozilla.components.support.test.mock
16+
import mozilla.components.support.test.robolectric.testContext
17+
import mozilla.components.support.test.whenever
18+
import org.junit.Test
19+
import org.junit.runner.RunWith
20+
import org.mockito.Mockito.verify
21+
22+
@ExperimentalCoroutinesApi
23+
@RunWith(AndroidJUnit4::class)
24+
class AddonsPermissionsAdapterTest {
25+
26+
@Test
27+
fun `bind permissions`() {
28+
val textView: TextView = mock()
29+
val view = View(testContext)
30+
val permissions = listOf("permission")
31+
val style = Style(itemsTextColor = R.color.photonBlue40)
32+
val viewHolder = PermissionViewHolder(view, textView)
33+
34+
whenever(textView.context).thenReturn(testContext)
35+
36+
val adapter = AddonPermissionsAdapter(permissions, style)
37+
38+
adapter.onBindViewHolder(viewHolder, 0)
39+
40+
verify(textView).text = "permission"
41+
verify(textView).setTextColor(ContextCompat.getColor(testContext, style.itemsTextColor!!))
42+
}
43+
}

docs/changelog.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ permalink: /changelog/
1212
* [Gecko](https://github.com/mozilla-mobile/android-components/blob/master/buildSrc/src/main/java/Gecko.kt)
1313
* [Configuration](https://github.com/mozilla-mobile/android-components/blob/master/buildSrc/src/main/java/Config.kt)
1414

15+
* **feature-addons**
16+
* Added `AddonPermissionsAdapter.Style` and `AddonsManagerAdapter.Style` classes to allow UI customization.
17+
1518
* **service-accounts-push**
1619
* Fixed a bug where the push subscription was incorrectly cached and caused some `GeneralError`s.
1720

0 commit comments

Comments
 (0)