1
0
Fork 0

Use StateListDrawable & resources w/ TabCollection

master
Tiger Oakes 2019-07-15 11:43:30 -04:00 committed by Emily Kager
parent 451093fd9e
commit 6da6ddb095
11 changed files with 112 additions and 83 deletions

View File

@ -8,23 +8,18 @@ import android.graphics.PorterDuff.Mode.SRC_IN
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import io.reactivex.Observer
import kotlinx.android.synthetic.main.collections_list_item.view.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import org.mozilla.fenix.R
import org.mozilla.fenix.components.description
import org.mozilla.fenix.ext.getIconColor
import org.mozilla.fenix.home.sessioncontrol.Tab
import org.mozilla.fenix.home.sessioncontrol.TabCollection
import org.mozilla.fenix.utils.AdapterWithJob
import kotlin.coroutines.CoroutineContext
class SaveCollectionListAdapter(
val actionEmitter: Observer<CollectionCreationAction>
) : AdapterWithJob<CollectionViewHolder>() {
) : RecyclerView.Adapter<CollectionViewHolder>() {
private var tabCollections = listOf<TabCollection>()
private var selectedTabs: Set<Tab> = setOf()
@ -33,13 +28,13 @@ class SaveCollectionListAdapter(
val view = LayoutInflater.from(parent.context)
.inflate(CollectionViewHolder.LAYOUT_ID, parent, false)
return CollectionViewHolder(view, actionEmitter, adapterJob)
return CollectionViewHolder(view)
}
override fun onBindViewHolder(holder: CollectionViewHolder, position: Int) {
val collection = tabCollections[position]
holder.bind(collection)
holder.view.setOnClickListener {
holder.itemView.setOnClickListener {
collection.apply {
val action = CollectionCreationAction.SelectCollection(this, selectedTabs.toList())
actionEmitter.onNext(action)
@ -56,46 +51,19 @@ class SaveCollectionListAdapter(
}
}
class CollectionViewHolder(
val view: View,
actionEmitter: Observer<CollectionCreationAction>,
val job: Job
) :
RecyclerView.ViewHolder(view), CoroutineScope {
override val coroutineContext: CoroutineContext
get() = Dispatchers.IO + job
private var collection: TabCollection? = null
class CollectionViewHolder(view: View) : RecyclerView.ViewHolder(view) {
fun bind(collection: TabCollection) {
this.collection = collection
view.collection_item.text = collection.title
view.collection_description.text = collection.description(view.context)
itemView.collection_item.text = collection.title
itemView.collection_description.text = collection.description(itemView.context)
view.collection_icon.setColorFilter(
ContextCompat.getColor(
view.context,
getIconColor(collection.id)
),
itemView.collection_icon.setColorFilter(
collection.getIconColor(itemView.context),
SRC_IN
)
}
@Suppress("MagicNumber")
private fun getIconColor(id: Long): Int {
return when ((id % 5).toInt()) {
0 -> R.color.collection_icon_color_violet
1 -> R.color.collection_icon_color_blue
2 -> R.color.collection_icon_color_pink
3 -> R.color.collection_icon_color_green
4 -> R.color.collection_icon_color_yellow
else -> R.color.white_color
}
}
companion object {
const val LAYOUT_ID = R.layout.collections_list_item
const val maxTitleLength = 20
}
}

View File

@ -0,0 +1,24 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.ext
import android.content.Context
import androidx.annotation.ColorInt
import androidx.core.content.ContextCompat
import org.mozilla.fenix.R
import org.mozilla.fenix.home.sessioncontrol.TabCollection
import kotlin.math.abs
/**
* Selects one of the predefined collection icon colors based on the id.
*/
@ColorInt
fun TabCollection.getIconColor(context: Context): Int {
val iconColors = context.resources.obtainTypedArray(R.array.collection_icon_colors)
val index = abs(id % iconColors.length()).toInt()
val color = iconColors.getColor(index, ContextCompat.getColor(context, R.color.white_color))
iconColors.recycle()
return color
}

View File

@ -104,7 +104,7 @@ class SessionControlAdapter(
PrivateBrowsingDescriptionViewHolder.LAYOUT_ID -> PrivateBrowsingDescriptionViewHolder(view, actionEmitter)
NoContentMessageViewHolder.LAYOUT_ID -> NoContentMessageViewHolder(view)
CollectionHeaderViewHolder.LAYOUT_ID -> CollectionHeaderViewHolder(view)
CollectionViewHolder.LAYOUT_ID -> CollectionViewHolder(view, actionEmitter, adapterJob)
CollectionViewHolder.LAYOUT_ID -> CollectionViewHolder(view, actionEmitter)
TabInCollectionViewHolder.LAYOUT_ID -> TabInCollectionViewHolder(view, actionEmitter, adapterJob)
OnboardingHeaderViewHolder.LAYOUT_ID -> OnboardingHeaderViewHolder(view)
OnboardingSectionHeaderViewHolder.LAYOUT_ID -> OnboardingSectionHeaderViewHolder(view)

View File

@ -8,37 +8,29 @@ import android.content.Context
import android.graphics.PorterDuff.Mode.SRC_IN
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import io.reactivex.Observer
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.collection_home_list_row.*
import kotlinx.android.synthetic.main.collection_home_list_row.view.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import mozilla.components.browser.menu.BrowserMenuBuilder
import mozilla.components.browser.menu.item.SimpleBrowserMenuItem
import org.mozilla.fenix.R
import org.mozilla.fenix.ThemeManager
import org.mozilla.fenix.components.description
import org.mozilla.fenix.ext.getIconColor
import org.mozilla.fenix.ext.increaseTapArea
import org.mozilla.fenix.home.sessioncontrol.CollectionAction
import org.mozilla.fenix.home.sessioncontrol.SessionControlAction
import org.mozilla.fenix.home.sessioncontrol.TabCollection
import org.mozilla.fenix.home.sessioncontrol.onNext
import kotlin.coroutines.CoroutineContext
class CollectionViewHolder(
val view: View,
val actionEmitter: Observer<SessionControlAction>,
val job: Job,
override val containerView: View? = view
) :
RecyclerView.ViewHolder(view), LayoutContainer, CoroutineScope {
override val coroutineContext: CoroutineContext
get() = Dispatchers.IO + job
RecyclerView.ViewHolder(view), LayoutContainer {
private lateinit var collection: TabCollection
private var expanded = false
@ -70,6 +62,7 @@ class CollectionViewHolder(
}
}
view.clipToOutline = true
view.setOnClickListener {
handleExpansion(expanded)
}
@ -84,27 +77,20 @@ class CollectionViewHolder(
private fun updateCollectionUI() {
view.collection_title.text = collection.title
view.collection_description.text = collection.description(view.context)
val layoutParams = view.layoutParams as ViewGroup.MarginLayoutParams
view.isActivated = expanded
if (expanded) {
(view.layoutParams as ViewGroup.MarginLayoutParams).bottomMargin = 0
layoutParams.bottomMargin = 0
collection_title.setPadding(0, 0, 0, EXPANDED_PADDING)
view.background = ContextCompat.getDrawable(view.context, R.drawable.rounded_top_corners)
view.collection_description.visibility = View.GONE
view.chevron.setBackgroundResource(R.drawable.ic_chevron_up)
} else {
(view.layoutParams as ViewGroup.MarginLayoutParams).bottomMargin = COLLAPSED_MARGIN
view.background = ContextCompat.getDrawable(view.context, R.drawable.rounded_all_corners)
layoutParams.bottomMargin = COLLAPSED_MARGIN
view.collection_description.visibility = View.VISIBLE
view.chevron.setBackgroundResource(R.drawable.ic_chevron_down)
}
view.collection_icon.setColorFilter(
ContextCompat.getColor(
view.context,
getIconColor(collection.id)
),
collection.getIconColor(view.context),
SRC_IN
)
}
@ -117,19 +103,6 @@ class CollectionViewHolder(
}
}
@Suppress("MagicNumber")
private fun getIconColor(id: Long): Int {
val sessionColorIndex = (id % 5).toInt()
return when (sessionColorIndex) {
0 -> R.color.collection_icon_color_violet
1 -> R.color.collection_icon_color_blue
2 -> R.color.collection_icon_color_pink
3 -> R.color.collection_icon_color_green
4 -> R.color.collection_icon_color_yellow
else -> R.color.white_color
}
}
companion object {
const val buttonIncreaseDps = 16
const val EXPANDED_PADDING = 60

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<selector xmlns:android="http://schemas.android.com/apk/res/android" android:constantSize="true">
<item android:drawable="@drawable/rounded_top_corners" android:state_activated="true" />
<item android:drawable="@drawable/rounded_all_corners" />
</selector>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<selector xmlns:android="http://schemas.android.com/apk/res/android" android:constantSize="true">
<item android:drawable="@drawable/ic_chevron_up" android:state_activated="true" />
<item android:drawable="@drawable/ic_chevron_down" />
</selector>

View File

@ -2,8 +2,7 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="?above" />
<corners android:radius="@dimen/tab_corner_radius"/>
</shape>
</shape>

View File

@ -2,8 +2,7 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="?above" />
<corners android:topLeftRadius="@dimen/tab_corner_radius" android:topRightRadius="@dimen/tab_corner_radius" />
</shape>
</shape>

View File

@ -10,7 +10,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:background="@drawable/rounded_all_corners"
android:background="@drawable/collection_home_list_row_background"
android:clickable="true"
android:clipToPadding="false"
android:elevation="5dp"
@ -59,7 +59,7 @@
android:layout_height="6dp"
android:layout_marginTop="24dp"
android:layout_marginEnd="16dp"
android:background="@drawable/ic_chevron_down"
android:background="@drawable/ic_chevron"
android:contentDescription="@string/tab_menu"
app:layout_constraintEnd_toStartOf="@+id/collection_share_button"
app:layout_constraintStart_toEndOf="@+id/collection_title"

View File

@ -102,6 +102,13 @@
<color name="collection_icon_color_pink">@color/collection_icon_color_pink_light_theme</color>
<color name="collection_icon_color_green">@color/collection_icon_color_green_light_theme</color>
<color name="collection_icon_color_yellow">@color/collection_icon_color_yellow_light_theme</color>
<array name="collection_icon_colors">
<item>@color/collection_icon_color_violet</item>
<item>@color/collection_icon_color_blue</item>
<item>@color/collection_icon_color_pink</item>
<item>@color/collection_icon_color_green</item>
<item>@color/collection_icon_color_yellow</item>
</array>
<!-- Library buttons -->
<color name="library_sessions_icon_background">#B9F0FD</color>

View File

@ -0,0 +1,43 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.ext
import androidx.core.content.ContextCompat
import junit.framework.Assert.assertEquals
import mozilla.components.feature.tab.collections.TabCollection
import mozilla.components.support.test.mock
import mozilla.components.support.test.robolectric.testContext
import org.junit.Assert.assertNotEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.`when`
import org.mozilla.fenix.R
import org.mozilla.fenix.TestApplication
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
@RunWith(RobolectricTestRunner::class)
@Config(application = TestApplication::class)
class TabCollectionTest {
@Test
fun getIconColor() {
val color = mockTabCollection(100L).getIconColor(testContext)
// Color does not change
for (i in 0..99) {
assertEquals(color, mockTabCollection(100L).getIconColor(testContext))
}
// Returns a color for negative IDs
val defaultColor = ContextCompat.getColor(testContext, R.color.white_color)
assertNotEquals(defaultColor, mockTabCollection(-123L).getIconColor(testContext))
}
private fun mockTabCollection(id: Long): TabCollection {
val collection: TabCollection = mock()
`when`(collection.id).thenReturn(id)
return collection
}
}