Tag picker compose (#2849)

TagPickerActivity refactoring to Compose

1. state of the SearchBar moved to the viewModel
2. viewModel used as parameter to @Composables instead of number of separate ones
3. Import of TagPickerActivity is replaced by TagPickerActivityCompose through the project
pull/2850/head
Hady 3 weeks ago committed by GitHub
parent 782f4d6d7c
commit e6e275834a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -145,6 +145,7 @@ android {
}
}
/*
testOptions {
managedDevices {
localDevices {
@ -156,6 +157,7 @@ android {
}
}
}
*/
namespace = "org.tasks"
}

@ -618,7 +618,9 @@
android:taskAffinity=""
android:theme="@style/TranslucentDialog"/>
<activity android:name=".tags.TagPickerActivity" />
<!-- <activity android:name=".tags.TagPickerActivity" /> -->
<activity android:name=".tags.TagPickerActivityCompose" />
<activity
android:name=".etebase.EtebaseCalendarSettingsActivity"

@ -116,11 +116,8 @@ import org.tasks.markdown.MarkdownProvider
import org.tasks.preferences.Device
import org.tasks.preferences.Preferences
import org.tasks.sync.SyncAdapters
import org.tasks.tags.TagPickerActivity
import org.tasks.tasklist.DragAndDropRecyclerAdapter
import org.tasks.tasklist.SectionedDataSource
import org.tasks.tasklist.TaskViewHolder
import org.tasks.tasklist.ViewHolderFactory
import org.tasks.tags.TagPickerActivityCompose
import org.tasks.tasklist.*
import org.tasks.themes.ColorProvider
import org.tasks.themes.ThemeColor
import org.tasks.ui.TaskEditEvent
@ -250,7 +247,7 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
requireActivity().onBackPressedDispatcher.addCallback(requireActivity(), onBackPressed)
}
@ -658,10 +655,10 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
lifecycleScope.launch {
val modified = tagDataDao.applyTags(
taskDao
.fetch(data!!.getSerializableExtra(TagPickerActivity.EXTRA_TASKS) as ArrayList<Long>)
.fetch(data!!.getSerializableExtra(TagPickerActivityCompose.EXTRA_TASKS) as ArrayList<Long>)
.filterNot { it.readOnly },
data.getParcelableArrayListExtra(TagPickerActivity.EXTRA_PARTIALLY_SELECTED)!!,
data.getParcelableArrayListExtra(TagPickerActivity.EXTRA_SELECTED)!!
data.getParcelableArrayListExtra(TagPickerActivityCompose.EXTRA_PARTIALLY_SELECTED)!!,
data.getParcelableArrayListExtra(TagPickerActivityCompose.EXTRA_SELECTED)!!
)
taskDao.touch(modified)
}
@ -676,7 +673,7 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
}
override fun onMenuItemActionExpand(item: MenuItem): Boolean {
onBackPressed.isEnabled = true
onBackPressed.isEnabled = true
search.setOnQueryTextListener(this)
listViewModel.setSearchQuery("")
if (preferences.isTopAppBar) {
@ -686,7 +683,7 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
}
override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
onBackPressed.isEnabled = false
onBackPressed.isEnabled = false
search.setOnQueryTextListener(null)
listViewModel.setFilter(filter)
listViewModel.setSearchQuery(null)
@ -727,13 +724,13 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
R.id.edit_tags -> {
lifecycleScope.launch {
val tags = tagDataDao.getTagSelections(selected)
val intent = Intent(context, TagPickerActivity::class.java)
intent.putExtra(TagPickerActivity.EXTRA_TASKS, selected)
val intent = Intent(context, TagPickerActivityCompose::class.java)
intent.putExtra(TagPickerActivityCompose.EXTRA_TASKS, selected)
intent.putParcelableArrayListExtra(
TagPickerActivity.EXTRA_PARTIALLY_SELECTED,
TagPickerActivityCompose.EXTRA_PARTIALLY_SELECTED,
ArrayList(tagDataDao.getByUuid(tags.first!!)))
intent.putParcelableArrayListExtra(
TagPickerActivity.EXTRA_SELECTED, ArrayList(tagDataDao.getByUuid(tags.second!!)))
TagPickerActivityCompose.EXTRA_SELECTED, ArrayList(tagDataDao.getByUuid(tags.second!!)))
startActivityForResult(intent, REQUEST_TAG_TASKS)
}
true

@ -11,7 +11,7 @@ import kotlinx.coroutines.flow.update
import org.tasks.R
import org.tasks.compose.collectAsStateLifecycleAware
import org.tasks.compose.edit.TagsRow
import org.tasks.tags.TagPickerActivity
import org.tasks.tags.TagPickerActivityCompose
import org.tasks.ui.ChipProvider
import org.tasks.ui.TaskEditControlFragment
import javax.inject.Inject
@ -21,8 +21,8 @@ class TagsControlSet : TaskEditControlFragment() {
@Inject lateinit var chipProvider: ChipProvider
private fun onRowClick() {
val intent = Intent(context, TagPickerActivity::class.java)
intent.putParcelableArrayListExtra(TagPickerActivity.EXTRA_SELECTED, viewModel.selectedTags.value)
val intent = Intent(context, TagPickerActivityCompose::class.java)
intent.putParcelableArrayListExtra(TagPickerActivityCompose.EXTRA_SELECTED, viewModel.selectedTags.value)
startActivityForResult(intent, REQUEST_TAG_PICKER_ACTIVITY)
}
@ -48,7 +48,7 @@ class TagsControlSet : TaskEditControlFragment() {
if (requestCode == REQUEST_TAG_PICKER_ACTIVITY) {
if (resultCode == Activity.RESULT_OK && data != null) {
viewModel.selectedTags.value =
data.getParcelableArrayListExtra(TagPickerActivity.EXTRA_SELECTED)
data.getParcelableArrayListExtra(TagPickerActivityCompose.EXTRA_SELECTED)
?: ArrayList()
}
} else {

@ -31,8 +31,8 @@ import org.tasks.preferences.Preferences
import org.tasks.repeats.BasicRecurrenceDialog
import org.tasks.repeats.BasicRecurrenceDialog.Companion.EXTRA_RRULE
import org.tasks.repeats.RepeatRuleToString
import org.tasks.tags.TagPickerActivity
import org.tasks.tags.TagPickerActivity.Companion.EXTRA_SELECTED
import org.tasks.tags.TagPickerActivityCompose
import org.tasks.tags.TagPickerActivityCompose.Companion.EXTRA_SELECTED
import javax.inject.Inject
private const val FRAG_TAG_DEFAULT_LIST_SELECTION = "frag_tag_default_list_selection"
@ -108,7 +108,7 @@ class TaskDefaults : InjectingPreferenceFragment() {
findPreference(R.string.p_default_tags)
.setOnPreferenceClickListener {
lifecycleScope.launch {
val intent = Intent(context, TagPickerActivity::class.java)
val intent = Intent(context, TagPickerActivityCompose::class.java)
.putParcelableArrayListExtra(
EXTRA_SELECTED,
ArrayList(defaultTags())

@ -1,108 +0,0 @@
package org.tasks.tags
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.widget.EditText
import androidx.activity.viewModels
import androidx.core.widget.addTextChangedListener
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import org.tasks.R
import org.tasks.Strings.isNullOrEmpty
import org.tasks.billing.Inventory
import org.tasks.data.TagData
import org.tasks.databinding.ActivityTagPickerBinding
import org.tasks.extensions.addBackPressedCallback
import org.tasks.injection.ThemedInjectingAppCompatActivity
import org.tasks.themes.ColorProvider
import org.tasks.themes.Theme
import javax.inject.Inject
@AndroidEntryPoint
class TagPickerActivity : ThemedInjectingAppCompatActivity() {
@Inject lateinit var theme: Theme
@Inject lateinit var inventory: Inventory
@Inject lateinit var colorProvider: ColorProvider
private val viewModel: TagPickerViewModel by viewModels()
private var taskIds: ArrayList<Long>? = null
private lateinit var editText: EditText
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val intent = intent
taskIds = intent.getSerializableExtra(EXTRA_TASKS) as ArrayList<Long>?
if (savedInstanceState == null) {
intent.getParcelableArrayListExtra<TagData>(EXTRA_SELECTED)?.let {
viewModel.setSelected(
it,
intent.getParcelableArrayListExtra(EXTRA_PARTIALLY_SELECTED)
)
}
}
val binding = ActivityTagPickerBinding.inflate(layoutInflater)
editText = binding.searchInput.apply {
addTextChangedListener(
onTextChanged = { text, _, _, _ -> onSearch(text) }
)
}
setContentView(binding.root)
val toolbar = binding.toolbar
toolbar.setNavigationIcon(R.drawable.ic_outline_arrow_back_24px)
toolbar.setNavigationOnClickListener { onBackPressed() }
val themeColor = theme.themeColor
themeColor.applyToNavigationBar(this)
val recyclerAdapter = TagRecyclerAdapter(this, viewModel, inventory, colorProvider) { tagData, vh ->
onToggle(tagData, vh)
}
val recyclerView = binding.recyclerView
recyclerView.adapter = recyclerAdapter
(recyclerView.itemAnimator as DefaultItemAnimator?)!!.supportsChangeAnimations = false
recyclerView.layoutManager = LinearLayoutManager(this)
viewModel.observe(this) { recyclerAdapter.submitList(it) }
editText.setText(viewModel.text)
addBackPressedCallback {
if (isNullOrEmpty(viewModel.text)) {
val data = Intent()
data.putExtra(EXTRA_TASKS, taskIds)
data.putParcelableArrayListExtra(
EXTRA_PARTIALLY_SELECTED,
viewModel.getPartiallySelected()
)
data.putParcelableArrayListExtra(EXTRA_SELECTED, viewModel.getSelected())
setResult(Activity.RESULT_OK, data)
finish()
} else {
clear()
}
}
}
private fun onToggle(tagData: TagData, vh: TagPickerViewHolder) = lifecycleScope.launch {
val newTag = tagData.id == null
val newState = viewModel.toggle(tagData, vh.isChecked || newTag)
vh.updateCheckbox(newState)
if (newTag) {
clear()
}
}
private fun onSearch(text: CharSequence?) {
viewModel.search(text?.toString() ?: "")
}
private fun clear() {
editText.setText("")
}
companion object {
const val EXTRA_SELECTED = "extra_tags"
const val EXTRA_PARTIALLY_SELECTED = "extra_partial"
const val EXTRA_TASKS = "extra_tasks"
}
}

@ -0,0 +1,292 @@
package org.tasks.tags
/*
* TagPickerActivityCompose is a replacement for TagPickerActivity reimplemented
* using JetPack Compose framework.
*
* The modification eliminates TagRecycleAdapter.
* TriStateCheckbox from Compose is used instead of CheckBoxTriState.
* Source code for TagRecycleAdapter and TagPickerActivity were deleted because they became incompatible
* with modified TagPickerViewModel, but were not excluded from build due to the hilt logic.
*
* There is a tag "FeatureReady" in the git commits log which marks the state when the main logic was
* already implemented via Compose but but viewModel was modified with backward compatibility so to
* switch back to View implementation its enough to find lines like this
* //val intent = Intent(context, TagPickerActivity::class.java)
* val intent = Intent(context, TagPickerActivityCompose::class.java)
* in TaskListFragment.kt and TagsControlSet.kt and move comment mark to another line.
*/
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.material.TextField
import androidx.compose.material.TextFieldDefaults
import androidx.compose.material.TriStateCheckbox
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.state.ToggleableState
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewModelScope
import com.google.android.material.composethemeadapter.MdcTheme
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import org.tasks.R
import org.tasks.Strings
import org.tasks.billing.Inventory
import org.tasks.data.TagData
import org.tasks.extensions.addBackPressedCallback
import org.tasks.injection.ThemedInjectingAppCompatActivity
import org.tasks.themes.ColorProvider
import org.tasks.themes.CustomIcons
import org.tasks.themes.Theme
import javax.inject.Inject
@AndroidEntryPoint
class TagPickerActivityCompose : ThemedInjectingAppCompatActivity() {
@Inject lateinit var theme: Theme
@Inject lateinit var inventory: Inventory
@Inject lateinit var colorProvider: ColorProvider
private val viewModel: TagPickerViewModel by viewModels()
private var taskIds: ArrayList<Long>? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val intent = intent
taskIds = intent.getSerializableExtra(EXTRA_TASKS) as ArrayList<Long>?
if (savedInstanceState == null) {
val selected = intent.getParcelableArrayListExtra<TagData>(EXTRA_SELECTED)
if ( selected != null ) {
viewModel.setSelected(
selected, intent.getParcelableArrayListExtra<TagData>(EXTRA_PARTIALLY_SELECTED)
)
}
}
addBackPressedCallback { handleBackPressed() }
viewModel.search("")
setContent {
MdcTheme {
TagPicker(
viewModel,
onBackClicked = { handleBackPressed() },
getTagIcon = { tagData -> getIcon(tagData) },
getTagColor = { tagData -> getColor(tagData) }
)
} /* setContent */
}
} /* onCreate */
private fun handleBackPressed() {
if (Strings.isNullOrEmpty(viewModel.searchText.value)) {
val data = Intent()
data.putExtra(EXTRA_TASKS, taskIds)
data.putParcelableArrayListExtra(EXTRA_PARTIALLY_SELECTED, viewModel.getPartiallySelected())
data.putParcelableArrayListExtra(EXTRA_SELECTED, viewModel.getSelected())
setResult(Activity.RESULT_OK, data)
finish()
} else {
viewModel.search("")
}
} /* handleBackPressed */
private fun getColor(tagData: TagData): Color {
if (tagData.getColor() != 0) {
val themeColor = colorProvider.getThemeColor(tagData.getColor()!!, true)
if (inventory.purchasedThemes() || themeColor.isFree) {
return Color(themeColor.primaryColor)
}
}
return Color(getColor(R.color.icon_tint_with_alpha))
}
private fun getIcon(tagData: TagData): Int
{
val iconIndex = tagData.getIcon()
var iconResource = R.drawable.ic_outline_label_24px
if ( (iconIndex != null) && (iconIndex < 1000 || inventory.hasPro) ) {
iconResource = CustomIcons.getIconResId(iconIndex) ?: R.drawable.ic_outline_label_24px
}
return iconResource
}
/* Copy of the TagPickerActivity's companion object */
companion object {
const val EXTRA_SELECTED = "extra_tags"
const val EXTRA_PARTIALLY_SELECTED = "extra_partial"
const val EXTRA_TASKS = "extra_tasks"
}
}
@Composable
internal fun TagPicker(
viewModel: TagPickerViewModel,
onBackClicked: () -> Unit,
getTagIcon: (TagData) -> Int,
getTagColor: (TagData) -> Color
) {
Box ( modifier = Modifier.fillMaxSize() )
{
Column (modifier = Modifier.padding(horizontal = 12.dp)) {
Box( modifier = Modifier.fillMaxWidth() ) {
SearchBar(viewModel, onBackClicked)
}
Box (
modifier = Modifier.weight(1f)
) {
PickerBox(viewModel, viewModel.tagsList.observeAsState(initial = emptyList()), getTagIcon, getTagColor)
}
}
}
}
@Composable
internal fun SearchBar(
viewModel: TagPickerViewModel,
onBack: () -> Unit
) {
val searchPattern = remember { viewModel.searchText }
val invitation = LocalContext.current.getString(R.string.enter_tag_name)
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(
ImageVector.vectorResource(id = R.drawable.ic_outline_arrow_back_24px),
"Done",
modifier = Modifier
.padding(6.dp)
.clickable { onBack() }
)
TextField(
value = searchPattern.value,
onValueChange = { viewModel.search(it) },
placeholder = { Text(invitation) },
colors = TextFieldDefaults.textFieldColors(
textColor = MaterialTheme.colors.onBackground,
backgroundColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent
),
modifier = Modifier.padding(start = 6.dp)
)
}
} /* SearchBar */
@Composable
internal fun PickerBox (
viewModel: TagPickerViewModel,
tags: State<List<TagData>>,
getTagIcon: (TagData) -> Int = { R.drawable.ic_outline_label_24px },
getTagColor: (TagData) -> Color = { Color.Gray }
) {
val onClick: (TagData) -> Unit = {
viewModel.viewModelScope.launch {
viewModel.toggle(it, viewModel.getState(it) != ToggleableState.On) }
}
val newItem: (String) -> Unit = {
viewModel.viewModelScope.launch { viewModel.createNew(it); viewModel.search("") }
}
LazyColumn {
if (viewModel.tagToCreate.value != "") {
item(key = -1) {
val text = LocalContext.current.getString(R.string.new_tag) + " \"${viewModel.tagToCreate.value}\""
TagRow(
icon = ImageVector.vectorResource(R.drawable.ic_outline_add_24px),
iconColor = Color(LocalContext.current.getColor(R.color.icon_tint_with_alpha)),
text = text,
onClick = { newItem(viewModel.searchText.value) }
)
}
}
items( tags.value, key = { tag -> tag.id!! } )
{
val checked = remember { mutableStateOf ( viewModel.getState(it) ) }
val clickChecked: () -> Unit = { onClick(it); checked.value = viewModel.getState(it) }
TagRow(
icon = ImageVector.vectorResource(getTagIcon(it)),
iconColor = getTagColor(it),
text = it.name!!,
onClick = clickChecked
) {
TriStateCheckbox(
modifier = Modifier.padding(6.dp),
state = checked.value,
onClick = clickChecked
)
}
}
}
} /* PickerBox */
@Composable
internal fun TagRow (
icon: ImageVector,
iconColor: Color,
text: String,
onClick: () -> Unit,
checkBox: @Composable RowScope.() -> Unit = {}
) {
Row(modifier = Modifier.fillMaxWidth().clickable{ onClick() },
verticalAlignment = Alignment.CenterVertically,
) {
Icon(
imageVector = icon,
contentDescription = "",
modifier = Modifier.padding(6.dp),
tint = iconColor
)
Text(
text,
modifier = Modifier.weight(1f).padding(horizontal = 24.dp)
)
checkBox()
}
} /* TagRow */
/*
internal fun genTestTags(): List<TagData>
{
var idcc: Long = 1
return "alpha beta gamma delta kappa theta alfa1 beta1 gamma1 delta1 kappa1 theta1"
.split(" ")
.map { name -> TagData(name).also{ it.id = idcc++ } }
}
@Composable
@Preview(showBackground = true, backgroundColor = 0xffffff)
internal fun PickerBoxPreview() {
val list = remember { mutableStateOf( genTestTags() ) }
PickerBox(list, getTagColor = { Color.Green })
}
*/

@ -1,12 +1,16 @@
package org.tasks.tags
import androidx.lifecycle.*
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.state.ToggleableState
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import org.tasks.Strings.isNullOrEmpty
import org.tasks.data.TagData
import org.tasks.data.TagDataDao
import org.tasks.tags.CheckBoxTriStates.State
import javax.inject.Inject
@HiltViewModel
@ -17,12 +21,22 @@ class TagPickerViewModel @Inject constructor(
private val tags = MutableLiveData<List<TagData>>()
private val selected: MutableSet<TagData> = HashSet()
private val partiallySelected: MutableSet<TagData> = HashSet()
var text: String? = null
private set
val searchText: State<String>
get() = _searchText
private val _searchText = mutableStateOf("")
val tagToCreate: State<String>
get() = _tagToCreate
private val _tagToCreate = mutableStateOf("")
fun observe(owner: LifecycleOwner, observer: (List<TagData>) -> Unit) =
tags.observe(owner, observer)
/* The property to access selected tags list from the @Composable activity */
val tagsList: MutableLiveData<List<TagData>>
get() = tags
fun setSelected(selected: List<TagData>, partiallySelected: List<TagData>?) {
this.selected.addAll(selected)
if (partiallySelected != null) {
@ -35,16 +49,16 @@ class TagPickerViewModel @Inject constructor(
fun getPartiallySelected() = ArrayList(partiallySelected)
fun search(newText: String) {
if (!newText.equals(text, ignoreCase = true)) {
if (newText == "" || !newText.equals(_searchText.value, ignoreCase = true)) {
viewModelScope.launch {
val results = tagDataDao.searchTags(newText)
onUpdate(results.toMutableList())
onUpdate(newText, results.toMutableList())
}
}
text = newText
_searchText.value = newText
}
private fun onUpdate(results: MutableList<TagData>) {
private fun onUpdate(newText: String, results: MutableList<TagData>) {
val sorted = results
.sortedWith { l, r ->
val lSelected = selected.contains(l) || partiallySelected.contains(r)
@ -58,20 +72,21 @@ class TagPickerViewModel @Inject constructor(
}
}
.toMutableList()
if (!isNullOrEmpty(text) && !results.any { text.equals(it.name, ignoreCase = true) }) {
sorted.add(0, TagData(text))
}
if ( newText != "" && !results.any {newText.equals(it.name, ignoreCase = true) } )
_tagToCreate.value = newText
else
_tagToCreate.value = ""
tags.value = sorted
}
fun getState(tagData: TagData): State {
fun getState(tagData: TagData): ToggleableState {
if (partiallySelected.contains(tagData)) {
return State.PARTIALLY_CHECKED
return ToggleableState.Indeterminate
}
return if (selected.contains(tagData)) State.CHECKED else State.UNCHECKED
return if (selected.contains(tagData)) ToggleableState.On else ToggleableState.Off
}
suspend fun toggle(tagData: TagData, checked: Boolean): State {
suspend fun toggle(tagData: TagData, checked: Boolean): ToggleableState {
var tagData = tagData
if (tagData.id == null) {
tagData = TagData(tagData.name)
@ -80,10 +95,17 @@ class TagPickerViewModel @Inject constructor(
partiallySelected.remove(tagData)
return if (checked) {
selected.add(tagData)
State.CHECKED
ToggleableState.On
} else {
selected.remove(tagData)
State.UNCHECKED
ToggleableState.Off
}
}
suspend fun createNew(name: String) {
val tagData = TagData(name)
tagDataDao.createNew(tagData)
selected.add(tagData)
search("")
}
}

@ -1,55 +0,0 @@
package org.tasks.tags
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.AsyncListDiffer
import androidx.recyclerview.widget.RecyclerView
import org.tasks.R
import org.tasks.billing.Inventory
import org.tasks.data.TagData
import org.tasks.databinding.RowTagPickerBinding
import org.tasks.themes.ColorProvider
import org.tasks.themes.CustomIcons.getIconResId
internal class TagRecyclerAdapter(
private val context: Context,
private val viewModel: TagPickerViewModel,
private val inventory: Inventory,
private val colorProvider: ColorProvider,
private val callback: (TagData, TagPickerViewHolder) -> Unit
) : RecyclerView.Adapter<TagPickerViewHolder>() {
private val differ: AsyncListDiffer<TagData> = AsyncListDiffer(this, TagDiffCallback())
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
TagPickerViewHolder(
context,
RowTagPickerBinding.inflate(LayoutInflater.from(context), parent, false),
callback
)
override fun onBindViewHolder(holder: TagPickerViewHolder, position: Int) {
val tagData = differ.currentList[position]
holder.bind(tagData, getColor(tagData), getIcon(tagData), viewModel.getState(tagData))
}
override fun getItemCount(): Int = differ.currentList.size
private fun getColor(tagData: TagData): Int {
if (tagData.getColor() != 0) {
val themeColor = colorProvider.getThemeColor(tagData.getColor()!!, true)
if (inventory.purchasedThemes() || themeColor.isFree) {
return themeColor.primaryColor
}
}
return context.getColor(R.color.icon_tint_with_alpha)
}
private fun getIcon(tagData: TagData): Int? =
tagData.getIcon()?.takeIf { it < 1000 || inventory.hasPro }?.let { getIconResId(it) }
fun submitList(tagData: List<TagData>?) {
differ.submitList(tagData)
}
}
Loading…
Cancel
Save