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/2849/head
hady 2 months ago
parent cbb7a9a1fa
commit 741b4fa236

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

@ -92,7 +92,6 @@ import org.tasks.notifications.NotificationManager
import org.tasks.preferences.Device
import org.tasks.preferences.Preferences
import org.tasks.sync.SyncAdapters
import org.tasks.tags.TagPickerActivity
import org.tasks.tags.TagPickerActivityCompose
import org.tasks.tasklist.*
import org.tasks.themes.ColorProvider
@ -627,10 +626,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)
}
@ -705,14 +704,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)
val intent = Intent(context, TagPickerActivityCompose::class.java)
intent.putExtra(TagPickerActivity.EXTRA_TASKS, selected)
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,6 @@ 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
@ -22,9 +21,8 @@ class TagsControlSet : TaskEditControlFragment() {
@Inject lateinit var chipProvider: ChipProvider
private fun onRowClick() {
//val intent = Intent(context, TagPickerActivity::class.java)
val intent = Intent(context, TagPickerActivityCompose::class.java)
intent.putParcelableArrayListExtra(TagPickerActivity.EXTRA_SELECTED, viewModel.selectedTags.value)
intent.putParcelableArrayListExtra(TagPickerActivityCompose.EXTRA_SELECTED, viewModel.selectedTags.value)
startActivityForResult(intent, REQUEST_TAG_PICKER_ACTIVITY)
}
@ -50,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 {

@ -30,8 +30,8 @@ import org.tasks.preferences.Preferences
import org.tasks.repeats.BasicRecurrenceDialog
import org.tasks.repeats.BasicRecurrenceDialog.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"
@ -101,7 +101,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())

@ -32,7 +32,6 @@ import androidx.compose.material.TextField
import androidx.compose.material.TextFieldDefaults
import androidx.compose.material.TriStateCheckbox
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.State
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
@ -44,7 +43,6 @@ 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.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewModelScope
import com.google.android.material.composethemeadapter.MdcTheme
@ -69,48 +67,28 @@ class TagPickerActivityCompose : ThemedInjectingAppCompatActivity() {
private val viewModel: TagPickerViewModel by viewModels()
private var taskIds: ArrayList<Long>? = null
private val searchPattern = mutableStateOf("")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val intent = intent
taskIds = intent.getSerializableExtra(TagPickerActivity.EXTRA_TASKS) as ArrayList<Long>?
taskIds = intent.getSerializableExtra(EXTRA_TASKS) as ArrayList<Long>?
if (savedInstanceState == null) {
val selected = intent.getParcelableArrayListExtra<TagData>(TagPickerActivity.EXTRA_SELECTED)
val selected = intent.getParcelableArrayListExtra<TagData>(EXTRA_SELECTED)
if ( selected != null ) {
viewModel.setSelected(
selected, intent.getParcelableArrayListExtra<TagData>(TagPickerActivity.EXTRA_PARTIALLY_SELECTED)
selected, intent.getParcelableArrayListExtra<TagData>(EXTRA_PARTIALLY_SELECTED)
)
}
}
searchPattern.value = viewModel.text ?: ""
viewModel.search(searchPattern.value)
viewModel.search("")
setContent {
MdcTheme {
TagPicker(
searchPattern,
viewModel.tagsList.observeAsState(initial = emptyList()),
onTextChange = { newText ->
viewModel.search(newText); searchPattern.value = newText
},
viewModel,
onBackClicked = { onBackPressed() },
checkedState = {
when (viewModel.getState(it)) {
CheckBoxTriStates.State.CHECKED -> ToggleableState.On
CheckBoxTriStates.State.PARTIALLY_CHECKED -> ToggleableState.Indeterminate
else -> ToggleableState.Off
}
},
onTagClicked = {
onToggle(
it,
viewModel.getState(it) != CheckBoxTriStates.State.CHECKED
)
},
createTag = { onNewTag(it.name!!); searchPattern.value = "" },
getTagIcon = { tagData -> getIcon(tagData) },
getTagColor = { tagData -> getColor(tagData) }
)
@ -118,14 +96,8 @@ class TagPickerActivityCompose : ThemedInjectingAppCompatActivity() {
}
} /* onCreate */
private fun onToggle(tag: TagData, checked: Boolean) =
viewModel.viewModelScope.launch { viewModel.toggle(tag, checked) }
private fun onNewTag(name: String) =
viewModel.viewModelScope.launch { viewModel.createNew(name) }
override fun onBackPressed() {
if (Strings.isNullOrEmpty(viewModel.text)) {
if (Strings.isNullOrEmpty(viewModel.pattern.value)) {
val data = Intent()
data.putExtra(EXTRA_TASKS, taskIds)
data.putParcelableArrayListExtra(EXTRA_PARTIALLY_SELECTED, viewModel.getPartiallySelected())
@ -133,7 +105,6 @@ class TagPickerActivityCompose : ThemedInjectingAppCompatActivity() {
setResult(Activity.RESULT_OK, data)
finish()
} else {
searchPattern.value = ""
viewModel.search("")
}
} /* onBackPressed */
@ -158,7 +129,7 @@ class TagPickerActivityCompose : ThemedInjectingAppCompatActivity() {
return iconResource
}
/* Copy og the TagPickerActivity's companion object */
/* Copy of the TagPickerActivity's companion object */
companion object {
const val EXTRA_SELECTED = "extra_tags"
const val EXTRA_PARTIALLY_SELECTED = "extra_partial"
@ -168,13 +139,8 @@ class TagPickerActivityCompose : ThemedInjectingAppCompatActivity() {
@Composable
internal fun TagPicker(
searchPattern: MutableState<String>,
tagsList: State<List<TagData>>, /* tags selected in accordance to searchText */
onTextChange: (String) -> Unit = {},
viewModel: TagPickerViewModel,
onBackClicked: () -> Unit,
checkedState: (TagData) -> ToggleableState = { ToggleableState.Off },
onTagClicked: (TagData) -> Unit,
createTag: (TagData) -> Unit,
getTagIcon: (TagData) -> Int,
getTagColor: (TagData) -> Color
) {
@ -182,12 +148,12 @@ internal fun TagPicker(
{
Column (modifier = Modifier.padding(horizontal = 12.dp)) {
Box( modifier = Modifier.fillMaxWidth() ) {
SearchBar(searchPattern, onTextChange, onBackClicked)
SearchBar(viewModel, onBackClicked)
}
Box (
modifier = Modifier.weight(1f)
) {
PickerBox(tagsList, checkedState, onTagClicked, createTag, getTagIcon, getTagColor)
PickerBox(viewModel, viewModel.tagsList.observeAsState(initial = emptyList()), getTagIcon, getTagColor)
}
}
}
@ -195,10 +161,10 @@ internal fun TagPicker(
@Composable
internal fun SearchBar(
text: MutableState<String>,
onTextChange: (String) -> Unit,
viewModel: TagPickerViewModel,
onBack: () -> Unit
) {
val searchPattern = remember { viewModel.pattern }
val invitation = LocalContext.current.getString(R.string.enter_tag_name)
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(
@ -210,8 +176,8 @@ internal fun SearchBar(
)
TextField(
value = text.value,
onValueChange = { onTextChange(it); text.value = it },
value = searchPattern.value,
onValueChange = { viewModel.search(it) },
placeholder = { Text(invitation) },
colors = TextFieldDefaults.textFieldColors(
textColor = MaterialTheme.colors.onBackground,
@ -226,23 +192,39 @@ internal fun SearchBar(
@Composable
internal fun PickerBox(
viewModel: TagPickerViewModel,
tags: State<List<TagData>>,
getState: (TagData) -> ToggleableState = { ToggleableState.Off },
onClick: (TagData) -> Unit = {},
newItem: (TagData) -> Unit = {},
getTagIcon: (TagData) -> Int = { R.drawable.ic_outline_label_24px },
getTagColor: (TagData) -> Color = { Color.Gray }
) {
val getState: (TagData) -> ToggleableState = {
when (viewModel.getState(it)) {
CheckBoxTriStates.State.CHECKED -> ToggleableState.On
CheckBoxTriStates.State.PARTIALLY_CHECKED -> ToggleableState.Indeterminate
else -> ToggleableState.Off
}
}
val onClick: (TagData) -> Unit = {
viewModel.viewModelScope.launch {
viewModel.toggle(it, viewModel.getState(it) != CheckBoxTriStates.State.CHECKED) }
}
val newItem: (TagData) -> Unit = {
viewModel.viewModelScope.launch { viewModel.createNew(it.name!!); viewModel.search("") }
}
LazyColumn {
items( tags.value, key = { if (it.id == null) -1 else it.id!! } )
{
val checked = remember { mutableStateOf ( getState(it) ) }
Row(modifier = Modifier
.fillMaxWidth()
.clickable {
if ( it.id == null ) newItem(it)
else { onClick(it); checked.value = getState(it) }
},
.fillMaxWidth()
.clickable {
if (it.id == null) newItem(it)
else {
onClick(it); checked.value = getState(it)
}
},
verticalAlignment = Alignment.CenterVertically,
) {
Icon(
@ -255,13 +237,13 @@ internal fun PickerBox(
val text = LocalContext.current.getString(R.string.new_tag) + " \"${it.name!!}\""
Text( text,
modifier = Modifier
.padding(horizontal = 24.dp)
.clickable { newItem(it) } )
.padding(horizontal = 24.dp)
.clickable { newItem(it) } )
} else {
Text(it.name!!,
modifier = Modifier
.weight(1f)
.padding(horizontal = 24.dp)
.weight(1f)
.padding(horizontal = 24.dp)
)
TriStateCheckbox(
modifier = Modifier.padding(6.dp),
@ -274,10 +256,11 @@ internal fun PickerBox(
}
} /* PickerBox */
/*
internal fun genTestTags(): List<TagData>
{
var idcc: Long = 1
return "alfa beta gamma delta kappa theta alfa1 beta1 gamma1 delta1 kappa1 theta1"
return "alpha beta gamma delta kappa theta alfa1 beta1 gamma1 delta1 kappa1 theta1"
.split(" ")
.map { name -> TagData(name).also{ it.id = idcc++ } }
}
@ -287,4 +270,5 @@ internal fun genTestTags(): List<TagData>
internal fun PickerBoxPreview() {
val list = remember { mutableStateOf( genTestTags() ) }
PickerBox(list, getTagColor = { Color.Green })
}
}
*/

@ -1,15 +1,15 @@
package org.tasks.tags
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
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
@ -23,6 +23,10 @@ class TagPickerViewModel @Inject constructor(
var text: String? = null
private set
private val _pattern = mutableStateOf("")
val pattern: State<String>
get() = _pattern
fun observe(owner: LifecycleOwner, observer: (List<TagData>) -> Unit) =
tags.observe(owner, observer)
@ -49,6 +53,7 @@ class TagPickerViewModel @Inject constructor(
}
}
text = newText
_pattern.value = newText
}
private fun onUpdate(results: MutableList<TagData>) {
@ -65,20 +70,20 @@ class TagPickerViewModel @Inject constructor(
}
}
.toMutableList()
if (!isNullOrEmpty(text) && !results.any { text.equals(it.name, ignoreCase = true) }) {
sorted.add(0, TagData(text))
if (pattern.value != "" && !results.any { pattern.value.equals(it.name, ignoreCase = true) }) {
sorted.add(0, TagData(pattern.value))
}
tags.value = sorted
}
fun getState(tagData: TagData): State {
fun getState(tagData: TagData): CheckBoxTriStates.State {
if (partiallySelected.contains(tagData)) {
return State.PARTIALLY_CHECKED
return CheckBoxTriStates.State.PARTIALLY_CHECKED
}
return if (selected.contains(tagData)) State.CHECKED else State.UNCHECKED
return if (selected.contains(tagData)) CheckBoxTriStates.State.CHECKED else CheckBoxTriStates.State.UNCHECKED
}
suspend fun toggle(tagData: TagData, checked: Boolean): State {
suspend fun toggle(tagData: TagData, checked: Boolean): CheckBoxTriStates.State {
var tagData = tagData
if (tagData.id == null) {
tagData = TagData(tagData.name)
@ -87,10 +92,10 @@ class TagPickerViewModel @Inject constructor(
partiallySelected.remove(tagData)
return if (checked) {
selected.add(tagData)
State.CHECKED
CheckBoxTriStates.State.CHECKED
} else {
selected.remove(tagData)
State.UNCHECKED
CheckBoxTriStates.State.UNCHECKED
}
}

Loading…
Cancel
Save